/*____________________________________________________________________________ Copyright (C) 2002 PGP Corporation All rights reserved. $Id: CHTTPPGPKeyServer.cpp,v 1.8 2002/08/06 20:11:11 dallen Exp $ ____________________________________________________________________________*/ #include #include #include "pgpEventPriv.h" #include "pgpKeys.h" #include "pgpStrings.h" #include "pgpUtilities.h" #include "StPGPRefs.h" #include "CHTTPPGPKeyServer.h" //namespace { static const char * kQueryPrefix = "/pks/lookup?op=get&"; static const char * kUploadPath = "/pks/add"; static const char * kContentType = "application/x-www-form-urlencoded"; static const char * kPostPrefix = "keytext="; static const char * kHeadBeginTag = "(static_cast(&query))); ThrowIfPGPError_(pgpErr); path = static_cast(PGPNewData( PGPPeekContextMemoryMgr(mContext), strlen(kQueryPrefix) + strlen(query) + 1, kPGPMemoryMgrFlags_None)); if (path == 0) { ThrowPGPError_(kPGPError_OutOfMemory); } sprintf(path, "%s%s", kQueryPrefix, static_cast(query)); query.Free(); resultSize = GetPost(kPGPKeyServerState_Querying, path, &result); path.Free(); pgpErr = pgpEventKeyServer( mContext, mEventHandler, mEventHandlerData, this, kPGPKeyServerState_ProcessingResults); ThrowIfPGPError_(pgpErr); pgpErr = ProcessHTTPResult(result); ThrowIfPGPError_(pgpErr); pgpErr = PGPImport( mContext, outFoundKeys, PGPOInputBuffer( mContext, static_cast(result), resultSize), PGPOLastOption(mContext)); ThrowIfPGPError_(pgpErr); } catch (...) { if (mCanceled) { ThrowPGPError_(kPGPError_UserAbort); } else { throw; } } } void CHTTPPGPKeyServer::Upload( PGPKeyUploadPreference inSendPrivateKeys, PGPKeySetRef inKeysToUpload, PGPKeySetRef * outKeysThatFailed) { try { StPGPKeySetRef keysThatFailed; StPGPKeyListRef keyList; StPGPKeyIterRef keyIter; StPGPDataRef buffer; PGPSize bufSize; StPGPDataRef urlEncodedBuffer; PGPKeyDBObjRef key; PGPSize postPrefixSize = strlen(kPostPrefix); PGPBoolean partialFailure = false; PGPBoolean keyAlreadyExists = false; PGPError pgpErr; InitOperation(); if (inSendPrivateKeys == kPGPPrivateKeyAllowed) { if (mType != kPGPKeyServerProtocol_HTTPS) { ThrowPGPError_(kPGPError_ServerOperationRequiresTLS); } } pgpErr = PGPNewEmptyKeySet(PGPPeekKeySetKeyDB( inKeysToUpload ), &keysThatFailed); ThrowIfPGPError_(pgpErr); // Iterate through keys uploading them one at a time pgpErr = PGPOrderKeySet( inKeysToUpload, kPGPKeyOrdering_Any, FALSE, &keyList); ThrowIfPGPError_(pgpErr); pgpErr = PGPNewKeyIter(keyList, &keyIter); ThrowIfPGPError_(pgpErr); while ((pgpErr = PGPKeyIterNextKeyDBObj(keyIter, kPGPKeyDBObjType_Key, &key)) == kPGPError_NoErr) { pgpErr = PGPExport( mContext, PGPOExportKeyDBObj( mContext, key ), PGPOExportPrivateKeys( mContext, inSendPrivateKeys == kPGPPrivateKeyAllowed), PGPOAllocatedOutputBuffer( mContext, (void **) &buffer, MAX_PGPSize, &bufSize), PGPOExportFormat( mContext, kPGPExportFormat_Basic), PGPOLastOption(mContext)); ThrowIfPGPError_(pgpErr); urlEncodedBuffer = static_cast( PGPNewData( PGPPeekContextMemoryMgr(mContext), GetMaxURLEncodedBufferSize(bufSize) + postPrefixSize, kPGPMemoryMgrFlags_None)); if (urlEncodedBuffer == 0) { ThrowPGPError_(kPGPError_OutOfMemory); } strcpy(urlEncodedBuffer, kPostPrefix); bufSize = URLEncode(buffer, bufSize, static_cast(urlEncodedBuffer) + postPrefixSize) + postPrefixSize; buffer.Free(); bufSize = GetPost( kPGPKeyServerState_Uploading, kUploadPath, &buffer, kContentType, bufSize, urlEncodedBuffer); urlEncodedBuffer.Free(); pgpErr = pgpEventKeyServer( mContext, mEventHandler, mEventHandlerData, this, kPGPKeyServerState_ProcessingResults); ThrowIfPGPError_(pgpErr); pgpErr = ProcessHTTPResult(buffer); buffer.Free(); if (IsPGPError(pgpErr)) { PGPAddKey(key, keysThatFailed); partialFailure = true; if (pgpErr == kPGPError_ServerKeyAlreadyExists) { keyAlreadyExists = true; } } } if (IsPGPError(pgpErr) && (pgpErr != kPGPError_EndOfIteration)) { ThrowPGPError_(pgpErr); } if (partialFailure) { *outKeysThatFailed = keysThatFailed; keysThatFailed = kInvalidPGPKeySetRef; if (keyAlreadyExists) { ThrowPGPError_(kPGPError_ServerKeyAlreadyExists); } else { ThrowPGPError_(kPGPError_ServerPartialAddFailure); } } } catch (...) { if (mCanceled) { ThrowPGPError_(kPGPError_UserAbort); } else { throw; } } } PGPError CHTTPPGPKeyServer::ProcessHTTPResult( const char * inResult) { PGPError result = kPGPError_ServerUnknownResponse; char * currentItem; char endChar; char * endOfItem; // Skip over HTTP headers /* Sun C++ 5.0 header files prototype strstr to take * char* instead of const char* */ currentItem = strstr((char*)inResult, "\r\n\r\n"); if (currentItem != 0) { // Skip head endOfItem = FindSubStringNoCase(currentItem, kHeadBeginTag); if (endOfItem != 0) { endOfItem = FindSubStringNoCase(endOfItem, kHeadEndTag); if (endOfItem != 0) { currentItem = endOfItem + strlen(kHeadEndTag); } } // Skip title endOfItem = FindSubStringNoCase(currentItem, kTitleBeginTag); if (endOfItem != 0) { endOfItem = FindSubStringNoCase(endOfItem, kTitleEndTag); if (endOfItem != 0) { currentItem = endOfItem + strlen(kTitleEndTag); } } // Skip H1 endOfItem = FindSubStringNoCase(currentItem, kH1BeginTag); if (endOfItem != 0) { endOfItem = FindSubStringNoCase(endOfItem, kH1EndTag); if (endOfItem != 0) { currentItem = endOfItem + strlen(kH1EndTag); } } // Skip all prefix tags and whitespace PGPBoolean skip; PGPBoolean inTag = false; do { skip = false; if (isspace(*currentItem)) { skip = true; } else { if (inTag) { skip = true; if (*currentItem == '>') { inTag = false; } } else { if (*currentItem == '<') { skip = true; inTag = true; } } } if (skip) { currentItem++; } } while (skip); // To quote Mark McArdle's original code, "God willing and the // creek don't rise" we now point to the actual error message for (PGPUInt16 i = 0; kKeyServerMessages[i] != 0; i++) { if ( strncmp(kKeyServerMessages[i], currentItem, strlen(kKeyServerMessages[i])) == 0) { result = kKeyServerErrors[i]; break; } } // Set the error string if necessary if (result != kPGPError_NoErr) { endOfItem = currentItem; while ((*endOfItem != '\0') && (*endOfItem != '\x0D') && (*endOfItem != '\x0A') && (*endOfItem != '<')) { endOfItem++; } endChar = *endOfItem; *endOfItem = '\0'; SetErrorString(currentItem); *endOfItem = endChar; } } return result; }