Serve & Receive Files

Summary

An easy way to Serve & Receive Files using the Personal Web Sharing built into OS X on Macintosh.

Introduction

Using your web server to send and receive files.

The code

Now the code to start a file moving from your box is:

int main (int argc, const char * argv[]) {
  NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
  char *queryString = getenv("QUERY_STRING");
  char *methodString = getenv("REQUEST_METHOD");  
  char *remoteHostString = getenv("REMOTE_HOST");
  char *remoteAddrString = getenv("REMOTE_ADDR");

  if (methodString && 0 == strcmp(methodString, "POST")) {
    PostSave(queryString);
  } else {
    GetReply(queryString, (remoteHostString ? remoteHostString : remoteAddrString));
  }

  [pool release];
  return 0;
}

Now we'll write PostSave and GetReply.

static void GetReply(char *queryString, char *remoteHostString) {
const char *format = 
"Content-type: text/html\r\n\r\n"
"<html>\n"
"<head>\n"
"<title>%s</title>\n"
"</head>\n"
"<body>\n"
"<h1>%s</h1>\n"
"<p>HOST=%s</p>\n"  // <example of using environment variable.
"<p>Catcher OK</p>\n"
"</body>\n"
"</html>\n";
  if (nil == queryString) {
    queryString = "";
  }
  if (nil == remoteHostString) {
    remoteHostString = "";
  }
  printf(format, queryString, queryString, remoteHostString);
}


static void PostSave(char *queryString) {
  const char *path = YourUniquePathPathForSave();
  
  BOOL isOK = YES;
  int i = 0;
  FILE *outFile = fopen(path, "w");
  if (outFile) {
    int c;
    for(i = 0; isOK && EOF != (c = getchar()); ++i) {
      isOK = (EOF != fputc(c, outFile));
    }
  }
  fclose(outFile);
  PutReply(i, (isOK ? 0 : errno));
}


static const char *Path() {
  NSDate *date = [NSDate date];
  NSDateFormatter *formatter = [[[NSDateFormatter alloc] init] autorelease];
  [formatter setFormatterBehavior:NSDateFormatterBehavior10_4];
  [formatter setDateFormat:@"yyyy_MM_dd_HH_mm_ss"];
  NSMutableString *now = [NSMutableString stringWithString:[formatter stringFromDate:date]];
  [now replaceOccurrencesOfString:@"/" 
      withString:@"_" options:0 range:NSMakeRange(0, [now length])];
  [now replaceOccurrencesOfString:@":" 
      withString:@"_" options:0 range:NSMakeRange(0, [now length])];
  [now replaceOccurrencesOfString:@" " 
      withString:@"_" options:0 range:NSMakeRange(0, [now length])];
  // Must be writable by the "www" user
  NSString *path = [NSString stringWithFormat:@"/Logger/Logs/%@", now];
  
  return [path UTF8String];
}

static void PutReply(int count, int ecode) {
const char *format = 
"Content-type: text/html\r\n\r\n"
"<html>\n"
"<head>\n"
"<title>%s</title>\n"
"</head>\n"
"<body>\n"
"<h1>%s</h1>\n"
"<p>error code=%d (should be 0)</p>"
"<p>count written:%d</p>\n"
"</body>\n"
"</html>\n";
  printf(format, "Thank You", "Thank You", ecode, count);
}

Verify that Personal Web Sharing works

Turn Personal Web Sharing on in the Sharing panel of System Preferences

Test: going to http://localhost/ in a web browser should open a default Apache page.

(If the test failed, check in Console.)

Install the CGI

Test: going to http://localhost/cgi-bin/catcher.cgi?Hi+There should return a catcher.cgi greeting

create the target directory: catcher.cgi writes to /Logger/Logs, in Terminal, I did:

sudo ln -s /Users/turbozen/Public /Logger
cd /Users/turbozen/Public
mkdir Logs
chmod 777 Logs 

Test:

In Terminal:

curl -d "This is a post test" "http://localhost/cgi-bin/catcher.cgi?Post+Test"

See Also

Further Work

Google App Engine is a free place to put your server.

Dreamhost.com is a paid hosting service that gives you access to a gcc compiler for compiling cgi's.

A real program would worry about malicious users reading files ent by other users, or filling up the machine with junk (Denial of Service Attack).

Conclusion

This page has presented OS X Cocoa code for pushing a disk file to the web.