3 * Copyright (c) 2008, Blue Static <http://www.bluestatic.org>
5 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
6 * General Public License as published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
10 * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
13 * You should have received a copy of the GNU General Public License along with this program; if not,
14 * write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
17 #import "AppController.h"
19 #import <sys/socket.h>
23 #include <WebKit/WebKit.h>
26 @interface AppController (Private
)
27 - (void)setStatus
:(NSString
*)msg isError
:(BOOL)error
;
29 - (void)readChannel
:(LIBSSH2_CHANNEL
*)channel
;
30 - (void)uploadAndPrint
:(id)sender
;
32 - (NSString
*)getUploadSafeName
:(NSString
*)name
;
36 @implementation AppController
39 * Set up the printer list
43 if (self = [super init
])
45 printers
= [[NSArray alloc
] initWithContentsOfFile
:[[NSBundle mainBundle
] pathForResource
:@
"Printers" ofType
:@
"plist"]];
60 * Sets the status text
62 - (void)setStatus
:(NSString
*)msg isError
:(BOOL)error
64 [status setStringValue
:msg
];
67 [status setTextColor
:[NSColor redColor
]];
68 [progress stopAnimation
:self];
69 [goButton setEnabled
:YES
];
73 [status setTextColor
:[NSColor blackColor
]];
83 NSString
*helpPath
= [[NSBundle mainBundle
] pathForResource
:@
"Help" ofType
:@
"html"];
84 [[helpWebView mainFrame
] loadRequest
:[NSURLRequest requestWithURL
:[NSURL fileURLWithPath
:helpPath
]]];
86 [window makeKeyAndOrderFront
:self];
88 NSUserDefaults
*defaults
= [NSUserDefaults standardUserDefaults
];
89 NSString
*hash
= [[[NSBundle mainBundle
] infoDictionary
] valueForKey
:@
"CFBundleVersion"];
90 if (![[defaults valueForKey
:@
"LastVersionHelpWindow"] isEqualToString
:hash
])
92 [helpWindow makeKeyAndOrderFront
:self];
93 [defaults setObject
:hash forKey
:@
"LastVersionHelpWindow"];
98 * Reads through a channel (in non-blocking) mode until there is no more left to read
99 * and then it returns. This calls sleep(1) so that the channel can have time to process.
100 * Be sure this is threaded otherwis the interface will stall.
102 - (void)readChannel
:(LIBSSH2_CHANNEL
*)channel
104 libssh2_channel_set_blocking(channel
, 0);
110 memset(&buf
, '\0', sizeof(buf
));
111 numbytes
= libssh2_channel_read(channel
, buf
, sizeof(buf
));
113 NSLog(@
"SSH buffer: %s", buf
);
114 } while (libssh2_poll_channel_read(channel
, 0));
118 * Sends an item to the printer
120 - (IBAction
)print
:(id)sender
122 [NSThread detachNewThreadSelector
:@selector(uploadAndPrint
:) toTarget
:self withObject
:sender
];
126 * Opens an SSH session, creates a SCP channel to upload the file, followed by a shell channel
129 - (void)uploadAndPrint
:(id)sender
131 NSAutoreleasePool
*pool
= [[NSAutoreleasePool alloc
] init
];
133 [progress startAnimation
:self];
134 [progress setHidden
:NO
];
135 [status setHidden
:NO
];
136 [goButton setEnabled
:NO
];
139 struct stat fileInfo
;
140 NSString
*uploadName
= [self getUploadSafeName
:[dragRegion filePath
]];
141 const char *fileName
= [uploadName UTF8String
];
143 [self setStatus
:@
"Connecting to acs.bu.edu" isError
:NO
];
144 struct sockaddr_in sin
;
145 int sock
= socket(AF_INET
, SOCK_STREAM
, 0);
146 sin.sin_port
= htons(22);
147 sin.sin_family
= AF_INET
;
149 struct hostent
*host
= gethostbyname("acs.bu.edu");
150 memcpy(&sin.sin_addr
, host
->h_addr_list
[0], host
->h_length
);
152 if (stat([[dragRegion filePath
] UTF8String
], &fileInfo
))
154 return [self setStatus
:@
"Invalid file selected" isError
:YES
];
157 if (connect(sock
, (struct sockaddr
*)(&sin
), sizeof(struct sockaddr_in
)) != 0)
159 return [self setStatus
:@
"Could not connect to acs.bu.edu" isError
:YES
];
162 LIBSSH2_SESSION
*ssh
= libssh2_session_init();
165 return [self setStatus
:@
"Failed to initialize SSH context" isError
:YES
];
168 if (libssh2_session_startup(ssh
, sock
))
170 return [self setStatus
:@
"Could not tunnel over SSH" isError
:YES
];
173 if (libssh2_userauth_password(ssh
, [[username stringValue
] UTF8String
], [[password stringValue
] UTF8String
]))
175 [self setStatus
:@
"Bad username/password" isError
:YES
];
179 LIBSSH2_CHANNEL
*channel
= libssh2_scp_send(ssh
, fileName
, 0755, (unsigned long)fileInfo.st_size
);
182 [self setStatus
:@
"Unable to open upload SCP session" isError
:YES
];
186 [self setStatus
:@
"Uploading file..." isError
:NO
];
188 localFile
= fopen([[dragRegion filePath
] UTF8String
], "r");
191 int numread
, numwrote
;
194 numread
= fread(buf
, 1, sizeof(buf
), localFile
);
203 numwrote
= libssh2_channel_write(channel
, pbuf
, numread
);
206 } while (numwrote
> 0);
210 [self setStatus
:@
"File uploaded!" isError
:NO
];
212 libssh2_channel_send_eof(channel
);
213 libssh2_channel_wait_eof(channel
);
214 libssh2_channel_wait_closed(channel
);
215 libssh2_channel_free(channel
);
218 channel
= libssh2_channel_open_session(ssh
);
221 [self setStatus
:@
"Could not open SSH channel for printing" isError
:YES
];
225 if (libssh2_channel_request_pty(channel
, "vanilla"))
227 [self setStatus
:@
"Could not open ANSI TTY" isError
:YES
];
231 if (libssh2_channel_shell(channel
))
233 [self setStatus
:@
"Failed to open remote shell" isError
:YES
];
237 [self setStatus
:@
"Opened remote SSH shell" isError
:NO
];
240 [self readChannel
:channel
];
242 // ACS outage messages --> skip some more
243 char *delay
= "q\r\n\0";
244 libssh2_channel_write(channel
, delay
, sizeof(char) * strlen(delay
));
245 [self readChannel
:channel
];
247 // send the job to lpr
250 NSString
*printer
= [[printersController selection
] valueForKey
:@
"unixName"];
251 cmd
= (char *)[[NSString stringWithFormat
:@
"lpr -m -#%d -P%@ %s\r\n\0", [[copiesButtons selectedCell
] tag
], printer
, fileName
] UTF8String
];
253 if ([[copiesButtons selectedCell
] tag
] == 1)
254 cmd
= "touch __PRINT_1__\r\n\0";
256 cmd
= "touch __PRINT_2__\r\n\0";
258 libssh2_channel_write(channel
, cmd
, sizeof(char) * strlen(cmd
));
259 [self readChannel
:channel
];
261 // remove our temp file
262 cmd
= (char *)[[NSString stringWithFormat
:@
"rm -f %s\r\n\0", fileName
] UTF8String
];
263 libssh2_channel_write(channel
, cmd
, sizeof(char) * strlen(cmd
));
264 [self readChannel
:channel
];
266 [self setStatus
:[NSString stringWithFormat
:@
"Printed %@", uploadName
] isError
:NO
];
268 [dragRegion clearFile
];
270 libssh2_channel_send_eof(channel
);
271 libssh2_channel_eof(channel
);
272 libssh2_channel_close(channel
);
277 //libssh2_channel_free(channel);
280 libssh2_session_disconnect(ssh
, "Normal disconnect.");
281 libssh2_session_free(ssh
);
285 [progress stopAnimation
:self];
291 * Returns an NSString that is safe for uploding onto the server
293 - (NSString
*)getUploadSafeName
:(NSString
*)name
295 NSMutableString
*safeName
= [NSMutableString string
];
297 name
= [name lastPathComponent
];
298 name
= [name stringByDeletingPathExtension
];
300 for (int i
= 0, j
= 0; i
< [name length
] && j
< 20; i
++)
302 char c
= [name characterAtIndex
:i
];
303 if ((c
>= 48 && c
<= 57) ||
(c
>= 65 && c
<= 90) ||
(c
>= 97 && 122) || c
== 95)
305 [safeName appendFormat
:@
"%c", c
];
310 if (j
> 0 && [safeName characterAtIndex
:j
- 1] != '-')
312 [safeName appendString
:@
"_"];
316 else if (c
>= 38 && c
<= 46)
318 if (j
> 0 && [safeName characterAtIndex
:j
- 1] != '_')
320 [safeName appendString
:@
"-"];
327 return [NSString stringWithFormat
:@
"~/%@.%d.pdf", safeName
, random()];
331 * Opens the print quota check page
333 - (IBAction
)helpCheckPQuota
:(id)sender
335 NSURL
*url
= [NSURL URLWithString
:@
"http://www.bu.edu/phpbin/myacs/"];
336 [[NSWorkspace sharedWorkspace
] openURL
:url
];
340 * Opens the link to the ACS help desk homepage
342 - (IBAction
)helpACS
:(id)sender
344 NSURL
*url
= [NSURL URLWithString
:@
"http://www.bu.edu/cc/"];
345 [[NSWorkspace sharedWorkspace
] openURL
:url
];