Remove the old version checking system
[printdrop.git] / Source / AppController.m
1 /*
2 * PrintDrop
3 * Copyright (c) 2008, Blue Static <http://www.bluestatic.org>
4 *
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.
8 *
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.
12 *
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
15 */
16
17 #import "AppController.h"
18 #import <libssh2.h>
19 #import <sys/socket.h>
20 #import <arpa/inet.h>
21 #include <netdb.h>
22 #include <stdlib.h>
23
24 @interface AppController (Private)
25 - (void)setStatus:(NSString *)msg isError:(BOOL)error;
26
27 - (void)readChannel:(LIBSSH2_CHANNEL *)channel;
28 - (void)uploadAndPrint:(id)sender;
29
30 - (NSString *)getUploadSafeName:(NSString *)name;
31 @end
32
33
34 @implementation AppController
35
36 /**
37 * Set up the printer list
38 */
39 - (id)init
40 {
41 if (self = [super init])
42 {
43 printers = [[NSArray alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Printers" ofType:@"plist"]];
44 }
45 return self;
46 }
47
48 /**
49 * Dealloc
50 */
51 - (void)dealloc
52 {
53 [printers release];
54 [super dealloc];
55 }
56
57 /**
58 * Sets the status text
59 */
60 - (void)setStatus:(NSString *)msg isError:(BOOL)error
61 {
62 [status setStringValue:msg];
63 if (error)
64 {
65 [status setTextColor:[NSColor redColor]];
66 [progress stopAnimation:self];
67 }
68 else
69 {
70 [status setTextColor:[NSColor blackColor]];
71 }
72 }
73
74 /**
75 * Reads through a channel (in non-blocking) mode until there is no more left to read
76 * and then it returns. This calls sleep(1) so that the channel can have time to process.
77 * Be sure this is threaded otherwis the interface will stall.
78 */
79 - (void)readChannel:(LIBSSH2_CHANNEL *)channel
80 {
81 libssh2_channel_set_blocking(channel, 0);
82
83 char buf[1024];
84 int numbytes;
85 do
86 {
87 memset(&buf, '\0', sizeof(buf));
88 numbytes = libssh2_channel_read(channel, buf, sizeof(buf));
89 sleep(1);
90 NSLog(@"SSH buffer: %s", buf);
91 } while (libssh2_poll_channel_read(channel, 0));
92 }
93
94 /**
95 * Sends an item to the printer
96 */
97 - (IBAction)print:(id)sender
98 {
99 [NSThread detachNewThreadSelector:@selector(uploadAndPrint:) toTarget:self withObject:sender];
100 }
101
102 /**
103 * Opens an SSH session, creates a SCP channel to upload the file, followed by a shell channel
104 * to queue up LPR
105 */
106 - (void)uploadAndPrint:(id)sender
107 {
108 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
109
110 [progress startAnimation:self];
111 [progress setHidden:NO];
112 [status setHidden:NO];
113
114 FILE *localFile;
115 struct stat fileInfo;
116 const char *fileName = [[self getUploadSafeName:[dragRegion filePath]] UTF8String];
117
118 [self setStatus:@"Connecting to acs.bu.edu" isError:NO];
119 struct sockaddr_in sin;
120 int sock = socket(AF_INET, SOCK_STREAM, 0);
121 sin.sin_port = htons(22);
122 sin.sin_family = AF_INET;
123
124 struct hostent *host = gethostbyname("acs.bu.edu");
125 memcpy(&sin.sin_addr, host->h_addr_list[0], host->h_length);
126
127 if (stat([[dragRegion filePath] UTF8String], &fileInfo))
128 {
129 return [self setStatus:@"Invalid file selected" isError:YES];
130 }
131
132 if (connect(sock, (struct sockaddr *)(&sin), sizeof(struct sockaddr_in)) != 0)
133 {
134 return [self setStatus:@"Could not connect to acs.bu.edu" isError:YES];
135 }
136
137 LIBSSH2_SESSION *ssh = libssh2_session_init();
138 if (ssh == NULL)
139 {
140 return [self setStatus:@"Failed to initialize SSH context" isError:YES];
141 }
142
143 if (libssh2_session_startup(ssh, sock))
144 {
145 return [self setStatus:@"Could not tunnel over SSH" isError:YES];
146 }
147
148 if (libssh2_userauth_password(ssh, [[username stringValue] UTF8String], [[password stringValue] UTF8String]))
149 {
150 [self setStatus:@"Bad username/password" isError:YES];
151 goto shutdown;
152 }
153
154 LIBSSH2_CHANNEL *channel = libssh2_scp_send(ssh, fileName, 0755, (unsigned long)fileInfo.st_size);
155 if (!channel)
156 {
157 [self setStatus:@"Unable to open upload SCP session" isError:YES];
158 goto shutdown;
159 }
160
161 [self setStatus:@"Uploading file..." isError:NO];
162
163 localFile = fopen([[dragRegion filePath] UTF8String], "r");
164 char buf[1024];
165 char *pbuf;
166 int numread, numwrote;
167 do
168 {
169 numread = fread(buf, 1, sizeof(buf), localFile);
170 if (numread <= 0)
171 {
172 break;
173 }
174
175 pbuf = buf;
176 do
177 {
178 numwrote = libssh2_channel_write(channel, pbuf, numread);
179 pbuf += numwrote;
180 numread -= numread;
181 } while (numwrote > 0);
182
183 } while(1);
184
185 [self setStatus:@"File uploaded!" isError:NO];
186
187 libssh2_channel_send_eof(channel);
188 libssh2_channel_wait_eof(channel);
189 libssh2_channel_wait_closed(channel);
190 libssh2_channel_free(channel);
191 channel = NULL;
192
193 channel = libssh2_channel_open_session(ssh);
194 if (!channel)
195 {
196 [self setStatus:@"Could not open SSH channel for printing" isError:YES];
197 goto shutdown;
198 }
199
200 if (libssh2_channel_request_pty(channel, "vanilla"))
201 {
202 [self setStatus:@"Could not open ANSI TTY" isError:YES];
203 goto shutdown;
204 }
205
206 if (libssh2_channel_shell(channel))
207 {
208 [self setStatus:@"Failed to open remote shell" isError:YES];
209 goto shutdown;
210 }
211
212 [self setStatus:@"Opened remote SSH shell" isError:NO];
213
214 // read the banner
215 [self readChannel:channel];
216
217 // ACS outage messages --> skip some more
218 char *delay = "q\r\n\0";
219 libssh2_channel_write(channel, delay, sizeof(char) * strlen(delay));
220 [self readChannel:channel];
221
222 // send the job to lpr
223 char *cmd;
224 #ifndef BLU_DEBUG
225 NSString *printer = [[printersController selection] valueForKey:@"unixName"];
226 cmd = (char *)[[NSString stringWithFormat:@"lpr -m -P%@ %s\r\n\0", printer, fileName] UTF8String];
227 #else
228 cmd = "touch __PRINT__\r\n\0";
229 #endif
230 libssh2_channel_write(channel, cmd, sizeof(char) * strlen(cmd));
231 [self readChannel:channel];
232
233 // remove our temp file
234 cmd = (char *)[[NSString stringWithFormat:@"rm -f %s\r\n\0", fileName] UTF8String];
235 libssh2_channel_write(channel, cmd, sizeof(char) * strlen(cmd));
236 [self readChannel:channel];
237
238 [self setStatus:@"Printed!" isError:NO];
239
240 libssh2_channel_send_eof(channel);
241 libssh2_channel_eof(channel);
242 libssh2_channel_close(channel);
243
244 shutdown:
245 if (channel)
246 {
247 //libssh2_channel_free(channel);
248 channel = NULL;
249 }
250 libssh2_session_disconnect(ssh, "Normal disconnect.");
251 libssh2_session_free(ssh);
252
253 close(sock);
254
255 [progress stopAnimation:self];
256
257 [pool release];
258 }
259
260 /**
261 * Returns an NSString that is safe for uploding onto the server
262 */
263 - (NSString *)getUploadSafeName:(NSString *)name
264 {
265 NSMutableString *n = [NSMutableString stringWithString:name];
266 [n replaceOccurrencesOfString:@" " withString:@"" options:NSBackwardsSearch range:NSMakeRange(0, [name length])];
267 name = (NSString *)n;
268 name = [name lastPathComponent];
269 name = [name stringByDeletingPathExtension];
270 if ([name length] > 20)
271 name = [name substringToIndex:20];
272
273 srandomdev();
274 return [NSString stringWithFormat:@"~/%@.%d.pdf", name, random()];
275 }
276
277 @end