Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
459 lines (385 sloc) 16.4 KB
/*
* ShareController:
* Copyright 2008, Erica Sadun
*
* All rights are retained. This code remains the trade secret and intellectual property of Erica Sadun.
*/
#import "ShareController.h"
#import "Utilities.h"
#import "MIMEHelper.h"
#import "NuZip.h"
@implementation ShareController
- (id) init
{
if (!(self = [super init])) return self;
self.title = @"Share Data";
serverStatus = STATUS_OFFLINE;
return self;
}
- (void) pleaseConnectToNetwork
{
NSMutableString *riz = [[NSMutableString alloc] init];
[riz appendString:@"<h2>Service could not be established</h2>"];
[riz appendString:@"<p>You are not connected to a network.</p>"];
[riz appendString:@"<br /><br />"];
[(UITextView *)self.view setContentToHTMLString:[riz autorelease]];
}
- (void) serviceWasEstablished
{
[(UITextView *)self.view setContentToHTMLString:[NSString stringWithFormat:@"<h2>Success!</h2><p>Your iPhone can now serve files over HTTP. To access this service, launch a browser from another computer or iPhone on the same network and navigate to the following address(es):</p><p>%@%@</p><p>This service remains active until you quit from this application.</p><p>Ports are random and you are assigned a new one each time you run this program.</p>",
[Utilities localAddressForPort:chosenPort] ? [Utilities localAddressForPort:chosenPort]: @"",
[Utilities localIPAddressForPort:chosenPort] ? [Utilities localIPAddressForPort:chosenPort] : @"" ]];
}
- (void) couldNotEstablishService
{
[(UITextView *)self.view setContentToHTMLString:@"<h2>Service could not be established</h2><p>This application could not establish the HTTP server at this time. Please try again later.</p><br /><br />"];
}
- (void) tryingToEstablishService
{
[(UITextView *)self.view setContentToHTMLString:@"<h2>Attempting to Establish Service</h2><p>Please wait.</p><br /><br />"];
}
- (void) stoppingService
{
[(UITextView *)self.view setContentToHTMLString:@"<h2>You stopped service.</h2><p>The HTTP server is no longer active.</p><br /><br />"];
}
- (void) youCanStartService
{
[(UITextView *)self.view setContentToHTMLString:@"<h2>Web Server</h2><p>Press <b>Start Service</b> to begin serving files from your iPhone to web browsers on your local network.</p><br /><br/>"];
}
- (NSString *) css
{
return @"<style>/* based on iui.css (c) 2007 by iUI Project Members */ body { margin: 0; font-family: Helvetica; background: #FFFFFF; color: #000000; overflow-x: hidden; -webkit-user-select: none; -webkit-text-size-adjust: none; } body > *:not(.toolbar) { display: none; position: absolute; margin: 0; padding: 0; left: 0; top: 45px; width: 100%; min-height: 372px; } body > *[selected=\"true\"] { display: block; } a[selected], a:active { background-color: #194fdb !important; background-repeat: no-repeat, repeat-x; background-position: right center, left top; color: #FFFFFF !important; } body > .toolbar { box-sizing: border-box; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; border-bottom: 1px solid #2d3642; border-top: 1px solid #6d84a2; padding: 10px; height: 45px; background: #6d84a2 repeat-x; } .toolbar > h1 { position: absolute; overflow: hidden; font-size: 20px; text-align: center; font-weight: bold; text-shadow: rgba(0, 0, 0, 0.4) 0px -1px 0; text-overflow: ellipsis; white-space: nowrap; color: #FFFFFF; margin: 1px 0 0 -120px; left: 50%; width: 240px; height: 45px; } body > ul > li { position: relative; margin: 0; border-bottom: 1px solid #E0E0E0; padding: 8px 0 8px 10px; font-size: 20px; font-weight: bold; list-style: none; } body > ul > li > a { margin: -8px 0 -8px -10px; padding: 8px 32px 8px 10px; text-decoration: none; color: inherit; } a[target=\"_replace\"] { box-sizing: border-box; -webkit-box-sizing: border-box; padding-top: 25px; padding-bottom: 25px; font-size: 18px; color: cornflowerblue; background-color: #FFFFFF; background-image: none; } body > .dialog { top: 0; width: 100%; min-height: 417px; z-index: 2; background: rgba(0, 0, 0, 0.8); padding: 0; text-align: right; } .dialog > fieldset { box-sizing: border-box; -webkit-box-sizing: border-box; width: 100%; margin: 0; border: none; border-top: 1px solid #6d84a2; padding: 10px 6px; background: #7388a5 repeat-x; } .dialog > fieldset > h1 { margin: 0 10px 0 10px; padding: 0; font-size: 20px; font-weight: bold; color: #FFFFFF; text-shadow: rgba(0, 0, 0, 0.4) 0px -1px 0; text-align: center; } .dialog > fieldset > label { position: absolute; margin: 16px 0 0 6px; font-size: 14px; color: #999999; } p { font-family: Helvetica; background: #FFFFFF; color: #000000; padding:15px; font-size: 20px; margin-left: 15%; margin-right: 15%; text-align: center; } </style>";
}
- (NSString *) createindex
{
NSMutableString *outdata = [[NSMutableString alloc] init];
[outdata appendString:@"<html>"];
[outdata appendFormat:@"<head><title>%@</title>\n", cwd];
[outdata appendString:@"<meta name=\"viewport\" content=\"width=320; initial-scale=1.0; maximum-scale=1.0; user-scalable=0;\"/>"];
[outdata appendString:[self css]];
[outdata appendString:@"<script type=\"application/x-javascript\">"];
[outdata appendString:@"window.onload = function() { setTimeout(function() {window.scrollTo(0,1);), 100); }"];
[outdata appendString:@"</script>"];
[outdata appendString:@"</head><body>"];
[outdata appendFormat:@"<div class=\"toolbar\"> <h1 id=\"pageTitle\">%@</h1> <a id=\"backButton\" class=\"button\" href=\"#\"></a> </div>", [cwd lastPathComponent]];
[outdata appendString:@"<ul id=\"home\" title=\"Files\" selected=\"true\">"];
if (![cwd isEqualToString:@"/"])
{
NSString *nwd = [cwd stringByDeletingLastPathComponent];
if (![nwd isEqualToString:@"/"])
[outdata appendFormat:@"<li><a href=\"%@/\">Parent Directory/</a></li>\n", nwd];
else
[outdata appendFormat:@"<li><a href=\"%@\">Parent Directory/</a></li>\n", nwd];
}
// Read in the strings
NSString *wd;
wd = cwd;
for (NSString *fname in [[NSFileManager defaultManager] directoryContentsAtPath:wd])
{
BOOL isDir;
NSString *cpath = [wd stringByAppendingPathComponent:fname];
[[NSFileManager defaultManager] fileExistsAtPath:cpath isDirectory:&isDir];
[outdata appendFormat:@"<li><a href=\"%@%@\">%@%@</a>%@</li>\n",
cpath,
isDir ? @"/" : @"",
fname,
isDir ? @"/" : @"",
isDir ? [NSString stringWithFormat:@"<a href=\"zzzzip%@.zip\">[zip]</a>", cpath] : @""
];
[cpath release];
}
[outdata appendString:@"</ul>"];
[outdata appendString:@"</body></html>\n"];
return outdata;
}
- (void) produceError: (NSString *) errorString forFD: (int) fd
{
NSString *outcontent = [NSString stringWithFormat:@"HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n"];
write (fd, [outcontent UTF8String], [outcontent length]);
[outcontent release];
NSMutableString *outdata = [[NSMutableString alloc] init];
[outdata appendString:@"<html>"];
[outdata appendString:@"<head><title>Error</title>\n"];
[outdata appendString:@"<meta name=\"viewport\" content=\"width=320; initial-scale=1.0; maximum-scale=1.0; user-scalable=0;\"/>"];
[outdata appendString:[self css]];
[outdata appendString:@"</head><body>"];
[outdata appendString:@"<div class=\"toolbar\"> <h1 id=\"pageTitle\">Error</h1> <a id=\"backButton\" class=\"button\" href=\"#\"></a> </div>"];
[outdata appendFormat:@"<p id=\"ErrorPara\" selected=\"true\"><br />%@<br /><br />Return to <a href=\"upload.html\">upload page</a> or <a href=\"/\">Main browser</a></p>", errorString];
[outdata appendString:@"</body></html>\n"];
write (fd, [outdata UTF8String], [outdata length]);
close(fd);
[outdata release];
}
// Serve files to GET requests
- (void) handleWebRequest:(int) fd
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
static char buffer[BUFSIZE+1];
int len = read(fd, buffer, BUFSIZE);
buffer[len] = '\0';
NSString *request = [NSString stringWithCString:buffer];
NSArray *reqs = [request componentsSeparatedByString:@"\n"];
NSString *getreq = [[reqs objectAtIndex:0] substringFromIndex:4];
NSRange range = [getreq rangeOfString:@"HTTP/"];
if (range.location == NSNotFound)
{
printf("Error: GET request was improperly formed\n");
close(fd);
return;
}
NSString *filereq = [[getreq substringToIndex:range.location] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
if ([filereq isEqualToString:@"/"])
{
cwd = filereq;
NSString *outcontent = [NSString stringWithFormat:@"HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n"];
write(fd, [outcontent UTF8String], [outcontent length]);
NSString *outdata = [self createindex];
write(fd, [outdata UTF8String], [outdata length]);
close(fd);
return;
}
if ([filereq isEqualToString:@"/favicon.ico"])
{
NSString *outcontent = [NSString stringWithFormat:@"HTTP/1.0 200 OK\r\nContent-Type: %@\r\n\r\n", @"image/vnd.microsoft.icon"];
write (fd, [outcontent UTF8String], [outcontent length]);
NSData *data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"favicon" ofType:@"ico"]];
if (!data)
{
printf("Error: favicon.ico not found.\n");
return;
}
write(fd, [data bytes], [data length]);
close(fd);
return;
}
filereq = [filereq stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
// Primary index.html
if ([filereq hasSuffix:@"/"])
{
cwd = filereq;
if (![[NSFileManager defaultManager] fileExistsAtPath:filereq])
{
printf("Error: folder not found.\n");
[self produceError:@"Requested folder was not found." forFD:fd];
return;
}
NSString *outcontent = [NSString stringWithFormat:@"HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n"];
write(fd, [outcontent UTF8String], [outcontent length]);
NSString *outdata = [self createindex];
write(fd, [outdata UTF8String], [outdata length]);
close(fd);
return;
}
NSString *mime = [MIMEHelper mimeForExt:[filereq pathExtension]];
if (!mime)
{
printf("Error recovering mime type.\n");
[self produceError:@"Sorry. This file type is not supported." forFD:fd];
return;
}
NSRange r = [filereq rangeOfString:@"zzzzip"];
if (r.location != NSNotFound)
{
NSString *path1 = [filereq substringFromIndex:r.location + 6];
NSString *path = [path1 substringToIndex:[path1 length] - 4];
printf("Zip request: %s\n", [path UTF8String]);
NSString *zipRequest = @"zip -o archive.zip";
NSDirectoryEnumerator *direnum = [[NSFileManager defaultManager] enumeratorAtPath:path];
NSString *eachFile;
while (eachFile = [direnum nextObject])
{
BOOL isDir;
NSString *fpath = [path stringByAppendingPathComponent:eachFile];
[[NSFileManager defaultManager] fileExistsAtPath:fpath isDirectory:&isDir];
if (!isDir) zipRequest = [zipRequest stringByAppendingFormat:@" %@",
[fpath stringByReplacingOccurrencesOfString:@" " withString: @"\\ "]];
}
// CFShow(zipRequest);
chdir([DOCUMENTS_FOLDER UTF8String]);
[NuZip zip:zipRequest];
// Output the file
NSString *outcontent = @"HTTP/1.0 200 OK\r\nContent-Type:application/x-compressed\r\n\r\n";
write (fd, [outcontent UTF8String], [outcontent length]);
NSData *data = [NSData dataWithContentsOfFile:@"archive.zip"];
if (!data)
{
printf("Error: file not found.\n");
[self produceError:@"File was not found. Please check the requested path and try again." forFD:fd];
return;
}
printf("Writing %d bytes from file\n", [data length]);
write(fd, [data bytes], [data length]);
close(fd);
return;
}
// Output the file
NSString *outcontent = [NSString stringWithFormat:@"HTTP/1.0 200 OK\r\nContent-Type: %@\r\n\r\n", mime];
write (fd, [outcontent UTF8String], [outcontent length]);
NSData *data = [NSData dataWithContentsOfFile:filereq];
if (!data)
{
printf("Error: file not found.\n");
[self produceError:@"File was not found. Please check the requested path and try again." forFD:fd];
return;
}
printf("Writing %d bytes from file\n", [data length]);
write(fd, [data bytes], [data length]);
close(fd);
[pool release];
}
// Begin serving data -- this is a private method called by startService
- (BOOL) isServing
{
return isServing;
}
// Shut down service
- (void) stopService
{
printf("Shutting down service\n");
[self stoppingService];
isServing = NO;
close(listenfd);
self.navigationItem.rightBarButtonItem = [[[UIBarButtonItem alloc]
initWithTitle:@"Start Service"
style:UIBarButtonItemStylePlain
target:self
action:@selector(startService)] autorelease];
}
// Server
- (void) listenForRequests
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
static struct sockaddr_in cli_addr;
socklen_t length = sizeof(cli_addr);
while (1 > 0) {
if (!isServing) return;
if ((socketfd = accept(listenfd, (struct sockaddr *)&cli_addr, &length)) < 0)
{
isServing = NO;
return;
}
[self handleWebRequest:socketfd];
}
[pool release];
}
// Begin serving data -- this is a private method called by startService
- (void) startServer
{
static struct sockaddr_in serv_addr;
// Set up socket
if((listenfd = socket(AF_INET, SOCK_STREAM,0)) < 0)
{
isServing = NO;
[self couldNotEstablishService];
return;
}
// Serve to a random port
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = 0;
// Bind
if(bind(listenfd, (struct sockaddr *)&serv_addr,sizeof(serv_addr)) <0)
{
isServing = NO;
[self couldNotEstablishService];
return;
}
// Find out what port number was chosen.
int namelen = sizeof(serv_addr);
if (getsockname(listenfd, (struct sockaddr *)&serv_addr, (void *) &namelen) < 0) {
close(listenfd);
isServing = NO;
return;
}
chosenPort = ntohs(serv_addr.sin_port);
// Listen
if(listen(listenfd, 64) < 0)
{
isServing = NO;
return;
}
self.navigationItem.rightBarButtonItem = NULL;
[self serviceWasEstablished];
[NSThread detachNewThreadSelector:@selector(listenForRequests) toTarget:self withObject:NULL];
}
- (void) showServiceAvailable
{
[self youCanStartService];
self.navigationItem.rightBarButtonItem = [[[UIBarButtonItem alloc]
initWithTitle:@"Start Service"
style:UIBarButtonItemStylePlain
target:self
action:@selector(startService)] autorelease];
}
/*
*
* Establish service
*
*/
// Start service
- (void) startService
{
if (isServing)
{
printf("Error: Already Serving!\n");
return;
}
isServing = NO;
close(listenfd);
if (![Utilities connectedToNetwork])
{
[self pleaseConnectToNetwork];
return;
}
self.navigationItem.rightBarButtonItem = NULL;
[self tryingToEstablishService];
ntries = 0;
isServing = YES;
[self startServer];
}
- (UIColor *)adBackgroundColor {
return [UIColor colorWithRed:0.271 green:0.271 blue:0.271 alpha:1.0f];
}
// All revenue helps support my iPhone hardware and dev membership
- (NSString *) publisherId
{
return @"a149c2995526aa7";
}
- (BOOL)useTestAd {
return NO;
}
- (void)loadView
{
UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 372.0f)];
[textView setEditable:NO];
[textView setFont:[UIFont systemFontOfSize:14.0f]];
#if !TARGET_IPHONE_SIMULATOR
AdMobView *ad = [AdMobView requestAdWithDelegate:self];
ad.frame = CGRectMake(0, 432, 320, 48);
ad.center = CGPointMake(160.0f, 416.0f - 24.0f);
[textView addSubview:ad];
#else
UILabel *ad = [[UILabel alloc] initWithFrame:CGRectMake(0, 432, 320, 48)];
ad.backgroundColor = [UIColor clearColor];
ad.textAlignment = UITextAlignmentCenter;
ad.text = @"[AdMob ad skipped for simulator]";
ad.center = CGPointMake(160.0f, 416.0f - 24.0f);
[textView addSubview:ad];
#endif
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 40.0f)];
label.backgroundColor = [UIColor clearColor];
label.center = CGPointMake(160.04, 416.0f - 45.0f - 25.0f);
label.textAlignment = UITextAlignmentCenter;
label.textColor = [UIColor lightGrayColor];
label.font = [UIFont boldSystemFontOfSize:14.0f];
label.numberOfLines = 2;
label.text = @"Ad revenues underwrite my iPhone\nhardware and $99/year dev membership";
[textView addSubview:label];
[label release];
self.view = textView;
[textView release];
[self showServiceAvailable];
isServing = NO;
cwd = @"/";
}
@end