//================================================================ // multform.cc // multipart/form-data型のFORMデータを受理するCGIプログラム // Copyright (C) Naoki Watanabe. 1996--2000. All rights reserved. //================================================================ #include #include #include #include #include #include // 送られてきたデータを動的確保したメモリにコピーして作業することにします。 // そのメモリの最大サイズを決めておくほうが安全でしょう。 #define MAX_MEMORY (1024*1024) // 1M bytes. // 2つのアドレスの間のデータの中から key の文字列を探し出す関数 char* memstr( char* begin, const char* end, const char* key ) { const size_t keylen = strlen(key); for( char* ptr=begin; &ptr[keylen-1]<=end; ptr++ ){ if( !memcmp( ptr, key, keylen ) ) return ptr; } return NULL; } // 2つの区切り文字列間のデータの範囲を調べる関数 int FindTerm( char*& begin, char*& end, char* separator ) { char* ptr; if( (ptr = memstr( begin, end, separator )) == NULL ) return 0; begin = &ptr[ strlen(separator)+2 ]; // beginには、"区切り文字列\r\n" の直後のアドレスが入る。 if( (ptr = memstr( begin, end, separator )) == NULL ) return 0; end = &ptr[ -3 ]; // endには、"\r\n区切り文字列" の直前のアドレスが入る。 return 1; } // ポインタが指しているところがファイルデータの先頭かを判断する関数 int IsFileTerm( char* begin, char* name, char* fname ) { return 2 == sscanf( begin, "Content-Disposition: form-data; " "name=\"%[^\"]\"; filename=\"%[^\"]\"", name, fname ); } // ポインタが指しているところが通常データの先頭かを判断する関数 int IsNormalTerm( char* begin, char* name ) { return 1 == sscanf( begin, "Content-Disposition: form-data; name=\"%[^\"]\"", name ); } // 指定範囲内の最初の空行を除外してい新しい範囲とする関数 void SkipBrankLine( char*& begin, char* end ) { const char* brank = "\r\n\r\n"; char* ptr; if( (ptr = memstr( begin, end, brank )) == NULL ){ printf("Broken data; not found a brank line.\n"); exit(1); } begin = &ptr[ strlen(brank) ]; // beginには空行の直後のアドレスが入る。 } // 指定範囲内のデータをファイルに保存する関数 void memtofile( const char* begin, const char* end, const char* fname ) { int fd; if( (fd = open( fname, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0600 )) < 0 ){ printf("Error in open:%s.\n", strerror(errno) ); exit(1); } int size=size_t(end)-size_t(begin)+1; write( fd, begin, size ); close(fd); } // 指定範囲内のデータを文字列配列に格納する関数 void memtostr( const char* begin, const char* end, char* str ) { int size=size_t(end)-size_t(begin)+1; strncpy( str, begin, size ); } //---- 試食用 main関数 int main( void ) { char name[32], value[1024], fname[64]; // ★MicroSoftのOSでは標準入力をバイナリモードに変更する作業が必要です。 // 以下のコードでバイナリモードになるかもしれません。 // _fmode = _O_BINARY; // _setmode( 0, _O_BINARY ); puts("Content-type: text/plain\n\n"); // データのサイズを調べ、大きすぎれば却下する。 u_long msize = atol(getenv("CONTENT_LENGTH")); if( msize > MAX_MEMORY ){ printf("Too large data to recieve.\n"); exit(1); } // メモリを確保し、その領域の先頭と末尾をポインタに記録する。 char* mbegin = new char [msize]; char* mend = &mbegin[msize-1]; // 標準入力からデータをメモリにまるごと読み込む。 fread( mbegin, 1, msize, stdin ); // fwrite( mbegin, 1, msize, stdout ); exit(0); // メモリの先頭にはMIMEの区切り文字列が格納されているはず。 // その区切り文字列を入手する。 char separator[256]; sscanf( mbegin, "%[^\r]", separator ); // 検索範囲としてメモリ全域を指定する。 char *begin=mbegin, *end=mend; // 検索範囲内にあるMIMEの項の範囲を探す。 while( FindTerm( begin, end, separator ) ){ // MIME区切り記号で分けられた、ひとつのMIME項目が検索範囲となる。 // 検索範囲冒頭にファイルデータの開始を意味するコードがあるかを確認する。 if( IsFileTerm( begin, name, fname ) ){ // 最初の空行を検索範囲から除外する。 SkipBrankLine(begin,end); // 検索範囲のデータをファイルに保存する。 memtofile( begin, end, "upload.dat" ); printf("Recieved file %s\n", fname ); } // 検索範囲冒頭に通常データの開始を意味するコードがあるかを確認する。 else if( IsNormalTerm( begin, name ) ){ // 最初の空行を検索範囲から除外する。 SkipBrankLine(begin,end); // 検索範囲を文字列にコピーする。 memtostr( begin, end, value ); printf("Recieved [%s] = [%s]\n", name, value ); }else{ // 正常ならここにはこないはず。 exit(0); } // 今のMIMEの項の終端を次の検索範囲の先頭として、次のMIMEの項の検索に備える。 begin = end; end = mend; } delete [] mbegin; return 0; }