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>
24 @interface AppController (Private
)
25 - (void)setStatus
:(NSString
*)msg isError
:(BOOL)error
;
27 - (void)readChannel
:(LIBSSH2_CHANNEL
*)channel
;
28 - (void)uploadAndPrint
:(id)sender
;
30 - (NSString
*)getUploadSafeName
:(NSString
*)name
;
34 @implementation AppController
37 * Set up the printer list
41 if (self = [super init
])
43 printers
= [[NSArray alloc
] initWithContentsOfFile
:[[NSBundle mainBundle
] pathForResource
:@
"Printers" ofType
:@
"plist"]];
44 [NSThread detachNewThreadSelector
:@selector(versionCheck
:) toTarget
:self withObject
:self];
59 * Checks and sees if the current version is the most up-to-date one
61 - (void)versionCheck
:(id)sender
63 NSAutoreleasePool
*pool
= [[NSAutoreleasePool alloc
] init
];
65 NSMutableString
*version
= [NSMutableString stringWithString
:[[[NSBundle mainBundle
] infoDictionary
] valueForKey
:@
"CFBundleShortVersionString"]];
66 [version replaceOccurrencesOfString
:@
" " withString
:@
"-" options
:NSLiteralSearch range
:NSMakeRange(0, [version length
])];
68 NSURL
*url
= [NSURL URLWithString
:[NSString stringWithFormat
:@
"http://www.bluestatic.org/versioncheck.php?prod=printdrop&ver=%@", version
]];
69 NSURLRequest
*request
= [NSURLRequest requestWithURL
:url cachePolicy
:NSURLRequestReloadIgnoringCacheData timeoutInterval
:10];
70 NSURLResponse
*response
;
71 NSData
*result
= [NSURLConnection sendSynchronousRequest
:request returningResponse
:&response error
:nil];
78 NSXMLDocument
*xml
= [[NSXMLDocument alloc
] initWithData
:result options
:0 error
:nil];
79 NSXMLNode
*comp
= [[xml rootElement
] childAtIndex
:0];
80 if ([[comp name
] isEqualToString
:@
"update"])
82 [updateString setStringValue
:[NSString stringWithFormat
:[updateString stringValue
], [comp stringValue
]]];
83 [updateWindow makeKeyAndOrderFront
:self];
92 * Opens the URL to the download page
94 - (IBAction
)openUpdateInformation
:(id)sender
96 [[NSWorkspace sharedWorkspace
] openURL
:[NSURL URLWithString
:@
"http://www.bluestatic.org/software/printdrop/"]];
100 * Sets the status text
102 - (void)setStatus
:(NSString
*)msg isError
:(BOOL)error
104 [status setStringValue
:msg
];
107 [status setTextColor
:[NSColor redColor
]];
108 [progress stopAnimation
:self];
112 [status setTextColor
:[NSColor blackColor
]];
117 * Reads through a channel (in non-blocking) mode until there is no more left to read
118 * and then it returns. This calls sleep(1) so that the channel can have time to process.
119 * Be sure this is threaded otherwis the interface will stall.
121 - (void)readChannel
:(LIBSSH2_CHANNEL
*)channel
123 libssh2_channel_set_blocking(channel
, 0);
129 memset(&buf
, '\0', sizeof(buf
));
130 numbytes
= libssh2_channel_read(channel
, buf
, sizeof(buf
));
132 NSLog(@
"SSH buffer: %s", buf
);
133 } while (libssh2_poll_channel_read(channel
, 0));
137 * Sends an item to the printer
139 - (IBAction
)print
:(id)sender
141 [NSThread detachNewThreadSelector
:@selector(uploadAndPrint
:) toTarget
:self withObject
:sender
];
145 * Opens an SSH session, creates a SCP channel to upload the file, followed by a shell channel
148 - (void)uploadAndPrint
:(id)sender
150 NSAutoreleasePool
*pool
= [[NSAutoreleasePool alloc
] init
];
152 [progress startAnimation
:self];
153 [progress setHidden
:NO
];
154 [status setHidden
:NO
];
157 struct stat fileInfo
;
158 const char *fileName
= [[self getUploadSafeName
:[dragRegion filePath
]] UTF8String
];
160 [self setStatus
:@
"Connecting to acs.bu.edu" isError
:NO
];
161 struct sockaddr_in sin
;
162 int sock
= socket(AF_INET
, SOCK_STREAM
, 0);
163 sin.sin_port
= htons(22);
164 sin.sin_family
= AF_INET
;
166 struct hostent
*host
= gethostbyname("acs.bu.edu");
167 memcpy(&sin.sin_addr
, host
->h_addr_list
[0], host
->h_length
);
169 if (stat([[dragRegion filePath
] UTF8String
], &fileInfo
))
171 return [self setStatus
:@
"Invalid file selected" isError
:YES
];
174 if (connect(sock
, (struct sockaddr
*)(&sin
), sizeof(struct sockaddr_in
)) != 0)
176 return [self setStatus
:@
"Could not connect to acs.bu.edu" isError
:YES
];
179 LIBSSH2_SESSION
*ssh
= libssh2_session_init();
182 return [self setStatus
:@
"Failed to initialize SSH context" isError
:YES
];
185 if (libssh2_session_startup(ssh
, sock
))
187 return [self setStatus
:@
"Could not tunnel over SSH" isError
:YES
];
190 if (libssh2_userauth_password(ssh
, [[username stringValue
] UTF8String
], [[password stringValue
] UTF8String
]))
192 [self setStatus
:@
"Bad username/password" isError
:YES
];
196 LIBSSH2_CHANNEL
*channel
= libssh2_scp_send(ssh
, fileName
, 0755, (unsigned long)fileInfo.st_size
);
199 [self setStatus
:@
"Unable to open upload SCP session" isError
:YES
];
203 [self setStatus
:@
"Uploading file..." isError
:NO
];
205 localFile
= fopen([[dragRegion filePath
] UTF8String
], "r");
208 int numread
, numwrote
;
211 numread
= fread(buf
, 1, sizeof(buf
), localFile
);
220 numwrote
= libssh2_channel_write(channel
, pbuf
, numread
);
223 } while (numwrote
> 0);
227 [self setStatus
:@
"File uploaded!" isError
:NO
];
229 libssh2_channel_send_eof(channel
);
230 libssh2_channel_wait_eof(channel
);
231 libssh2_channel_wait_closed(channel
);
232 libssh2_channel_free(channel
);
235 channel
= libssh2_channel_open_session(ssh
);
238 [self setStatus
:@
"Could not open SSH channel for printing" isError
:YES
];
242 if (libssh2_channel_request_pty(channel
, "vanilla"))
244 [self setStatus
:@
"Could not open ANSI TTY" isError
:YES
];
248 if (libssh2_channel_shell(channel
))
250 [self setStatus
:@
"Failed to open remote shell" isError
:YES
];
254 [self setStatus
:@
"Opened remote SSH shell" isError
:NO
];
257 [self readChannel
:channel
];
259 // ACS outage messages --> skip some more
260 char *delay
= "q\r\n\0";
261 libssh2_channel_write(channel
, delay
, sizeof(char) * strlen(delay
));
262 [self readChannel
:channel
];
264 // send the job to lpr
267 NSString
*printer
= [[printersController selection
] valueForKey
:@
"unixName"];
268 cmd
= (char *)[[NSString stringWithFormat
:@
"lpr -m -P%@ %s\r\n\0", printer
, fileName
] UTF8String
];
270 cmd
= "touch __PRINT__\r\n\0";
272 libssh2_channel_write(channel
, cmd
, sizeof(char) * strlen(cmd
));
273 [self readChannel
:channel
];
275 // remove our temp file
276 cmd
= (char *)[[NSString stringWithFormat
:@
"rm -f %s\r\n\0", fileName
] UTF8String
];
277 libssh2_channel_write(channel
, cmd
, sizeof(char) * strlen(cmd
));
278 [self readChannel
:channel
];
280 [self setStatus
:@
"Printed!" isError
:NO
];
282 libssh2_channel_send_eof(channel
);
283 libssh2_channel_eof(channel
);
284 libssh2_channel_close(channel
);
289 //libssh2_channel_free(channel);
292 libssh2_session_disconnect(ssh
, "Normal disconnect.");
293 libssh2_session_free(ssh
);
297 [progress stopAnimation
:self];
303 * Returns an NSString that is safe for uploding onto the server
305 - (NSString
*)getUploadSafeName
:(NSString
*)name
307 NSMutableString
*n
= [NSMutableString stringWithString
:name
];
308 [n replaceOccurrencesOfString
:@
" " withString
:@
"" options
:NSBackwardsSearch range
:NSMakeRange(0, [name length
])];
309 name
= (NSString
*)n
;
310 name
= [name lastPathComponent
];
311 name
= [name stringByDeletingPathExtension
];
312 if ([name length
] > 20)
313 name
= [name substringToIndex
:20];
316 return [NSString stringWithFormat
:@
"~/%@.%d.pdf", name
, random()];