root/capture-mod/trunk/CaptureSoapServer.cpp

Revision 1816, 21.6 kB (checked in by mbriggs, 3 months ago)

Changed SOAP Server to reflect database app

  • Property svn:executable set to *
Line 
1 /*
2 **Created by Xeno Kovah of the MITRE HoneyClient Project 5/20/2008
3 */
4
5 #include "CaptureSoapServer.h"
6 #include "soapH.h"
7 #include "capture.nsmap"
8 #include "Visitor.h"
9 #include "b64.h" //nice small 3rd party lib for base64 encode/decode
10
11 //Only global so that the destructors can be used in ~CaptureSoapServer
12 //Didn't go in the class definition because adding soapH.h to CaptureSoapServer.h caused compile issues
13 //that I didn't want to deal with
14 struct soap soap;
15
16 std::list<struct ns__regkey> regList;
17 std::list<struct ns__regkey> regDeallocList;
18 std::list<struct ns__processFile> fileList;
19 std::list<struct ns__processFile> fileDeallocList;
20 std::list<struct ns__osProcess> procList;
21 std::list<struct ns__osProcess> procDeallocList;
22 void dealloc_events(); //For cleaning up when we're done with them
23
24 CaptureSoapServer::CaptureSoapServer(Visitor* v, RegistryMonitor * r, FileMonitor * f, ProcessMonitor * p){
25     registryMonitor = r;
26     fileMonitor = f;
27     processMonitor = p;
28     CaptureSoapServerThread = new Thread(this);
29     CaptureSoapServerThread->start("CaptureSoapServer");
30 }
31
32 CaptureSoapServer::~CaptureSoapServer(){
33     soap_destroy(&soap);
34     soap_end(&soap);
35     soap_done(&soap);
36 }
37
38 void
39 CaptureSoapServer::run(){
40
41     char debug = 0;
42    SOCKET m, s; // master and slave sockets
43
44     onProcessEventConnection = processMonitor->connect_onProcessEvent(boost::bind(&CaptureSoapServer::onProcessEvent, this, _1, _2, _3, _4, _5, _6));
45     onRegistryEventConnection = registryMonitor->connect_onRegistryEvent(boost::bind(&CaptureSoapServer::onRegistryEvent, this, _1, _2, _3, _4, _5));
46     onFileEventConnection = fileMonitor->connect_onFileEvent(boost::bind(&CaptureSoapServer::onFileEvent, this, _1, _2, _3, _4, _5));
47
48    //The below code is taken mostly from the gsoap standalone server example page
49    soap_init(&soap);
50    //TODO: change this to the desired port
51    m = soap_bind(&soap, "0.0.0.0", 1234, 100);
52    if (m < 0)
53       soap_print_fault(&soap, stderr);
54    else
55    {
56       if(debug) fprintf(stderr, "Socket connection successful: master socket = %d\n", m);
57       for (int i = 1; ; i++)
58       {
59          s = soap_accept(&soap);
60          if (s < 0)
61          {
62             soap_print_fault(&soap, stderr);
63             break;
64          }
65          if(debug) fprintf(stderr, "%d: accepted connection from IP=%d.%d.%d.%d socket=%d\n", i,
66             (soap.ip >> 24)&0xFF, (soap.ip >> 16)&0xFF, (soap.ip >> 8)&0xFF, soap.ip&0xFF, s);
67          if (soap_serve(&soap) != SOAP_OK) // process RPC request
68             soap_print_fault(&soap, stderr); // print error
69          if(debug) fprintf(stderr, "you got served!\n");
70          soap_destroy(&soap); // clean up class instances
71          soap_end(&soap); // clean up everything and close socket
72          dealloc_events(); //Need to clean up any memory we malloced for events
73       }
74    }
75    soap_done(&soap); // close master socket and detach environment
76 }
77
78 //From RegistryMonitor.cpp
79 //registry event extra.at(0) == PID
80 //registry event extra.at(1) == name of registry value
81 //registry event extra.at(2) == registry value type
82 //registry event extra.at(3) == registry value data (if any)
83 void CaptureSoapServer::onRegistryEvent (wstring registryEventType, wstring time,
84                                         wstring processPath, wstring registryEventPath,
85                                         vector<wstring> extra)
86 {
87     char debug = 0;
88     if(debug) printf("CaptureSoapServer::onRegistryEvent got an event for time = %ls, length = %d\n", time.c_str(), time.length());
89
90     //now begins the arduous process of converting the values into char *s
91     ns__regkey_t r;
92     r.created_at = (char *)malloc(time.length()+1);
93     sprintf(r.created_at, "%ls", time.c_str());
94
95     r.event_type = (char *)malloc(registryEventType.length()+1);
96     sprintf(r.event_type, "%ls", registryEventType.c_str());
97
98     char * tmp = (char *)malloc(extra.at(0).length()+1);
99     sprintf(tmp, "%ls", extra.at(0).c_str());
100     r.pid = atoi(tmp);
101     free(tmp);
102
103     r.proc_name = (char *)malloc(processPath.length()+1);
104     sprintf(r.proc_name, "%ls", processPath.c_str());
105
106     r.name = (char *)malloc(registryEventPath.length()+1);
107     sprintf(r.name, "%ls", registryEventPath.c_str());
108
109     r.value_name = (char *)malloc(extra.at(1).length()+1);
110     sprintf(r.value_name, "%ls", extra.at(1).c_str());
111
112     r.value_type = (char *)malloc(extra.at(2).length()+1);
113     sprintf(r.value_type, "%ls", extra.at(2).c_str());
114
115     r.value = (char *)malloc(extra.at(3).length()+1);
116     sprintf(r.value, "%ls", extra.at(3).c_str());
117    
118     if(debug){
119         int * b = (int *)&r;
120         for(int i = 0; i < 8; i++){
121             printf("r[%d] = %#x\n", i, b[i]);
122         }
123     }
124
125     regList.push_back(r);
126     if(debug) printf("added one event to regList. Now there are %d elements in the list\n", regList.size());
127 }
128
129 //From FileMonitor.cpp
130 //file event extra.at(0) == PID
131 void CaptureSoapServer::onFileEvent(wstring fileEventType, wstring time,
132                                     wstring processPath, wstring fileEventPath,
133                                     vector<wstring> extra)
134 {
135     char debug = 0;
136     if(debug) printf("CaptureSoapServer::onFileEvent got an event for time = %ls\n", time.c_str());
137     ns__processFile_t f;
138     f.created_at = (char *)malloc(time.length()+1);
139     sprintf(f.created_at, "%ls", time.c_str());
140
141     f.event_type = (char *)malloc(fileEventType.length()+1);
142     sprintf(f.event_type, "%ls", fileEventType.c_str());
143
144     char * tmp = (char *)malloc(extra.at(0).length()+1);
145     sprintf(tmp, "%ls", extra.at(0).c_str());
146     f.pid = atoi(tmp);
147     free(tmp);
148
149     f.proc_name = (char *)malloc(processPath.length()+1);
150     sprintf(f.proc_name, "%ls", processPath.c_str());
151
152     f.name = (char *)malloc(fileEventPath.length()+1);
153     sprintf(f.name, "%ls", fileEventPath.c_str());
154
155     if(debug){
156         int * b = (int *)&f;
157         for(int i = 0; i < 5; i++){
158             printf("f[%d] = %#x\n", i, b[i]);
159         }
160     }
161     fileList.push_back(f);
162     if(debug) printf("added one event to fileList. Now there are %d elements in the list\n", fileList.size());
163
164 }
165
166 void CaptureSoapServer::onProcessEvent(BOOLEAN created, wstring time,
167                                         DWORD parentProcessId, wstring parentProcess,
168                                         DWORD processId, wstring process)
169 {
170     char debug = 0;
171     if(debug) printf("CaptureSoapServer::onProcessEvent got an event for time = %ls\n", time.c_str());
172     ns__osProcess_t p;
173     p.created_at = (char *)malloc(time.length()+1);
174     sprintf(p.created_at, "%ls", time.c_str());
175
176     p.event_type = (char *)malloc(11); //11 == max length == "terminated"
177     if(created){
178         sprintf(p.event_type, "created");
179     }
180     else{
181         sprintf(p.event_type, "terminated");
182     }
183
184     p.parent_pid = parentProcessId;
185
186     p.parent_name = (char *)malloc(parentProcess.length()+1);
187     sprintf(p.parent_name, "%ls", parentProcess.c_str());
188
189     p.pid = processId;
190
191     p.name = (char *)malloc(process.length()+1);
192     sprintf(p.name, "%ls", process.c_str());
193
194     procList.push_back(p);
195     if(debug) printf("added one event to procList. Now there are %d elements in the list\n", procList.size());
196
197 }
198
199
200 int ns__ping(struct soap *soap, char * a, char ** result)
201 {
202    printf("%s\n", a);
203    *result = "pong";
204
205    return SOAP_OK;
206 }
207
208 //Give it a url to browse to
209 int ns__visitURL(struct soap *soap, char * url, int &result){
210     char debug = 1;
211     wchar_t xURL[1024];
212     wsprintf(xURL, L"%hs", url);
213     //Build my own new-fangled Element to pass to Visitor:onServerEvent
214     typedef boost::signal<void (Element*)> signal_serverEvent;
215     Attribute att;
216     att.name = L"url";
217     att.value = xURL;
218     Element e;
219     e.name = L"visit";
220     e.attributes.push_back(att);
221     e.data = NULL;
222     e.dataLength = 0;
223     if(debug) printf("visitURL to %s\n", url);
224     EventController::getInstance()->notifyListeners(&e);
225
226     //TODO: We currently run the browser visit event as a black box.
227     //In the future we will want to be able to report back about events before
228     //it times out or it's cleanly done with the browse.
229     //NOTE: For now, telling the browser to run is not a blocking operation.
230     //The alternate way I had done it in the past (calling Visitor::onServerEvent directly IIRC)
231     //was a blocking way, and might need to be brought back, both for the blocking, and for
232     //being able to get richer information back
233
234     result = 1; //No meaning. Define some meaningful return values if desired
235
236     return SOAP_OK;
237 }
238
239 int ns__sendFileBase64(struct soap *soap, char * fileName, char * data, unsigned int encodedLength, unsigned int decodedLength, int &result){
240     char debug = 0;
241     if(debug) printf("in ns__sendFileBase64\n");
242
243     if(debug) printf("encodedLength = %d, decodedLength = %d, data[0][1][2][3] = %c%c%c%c\n", encodedLength, decodedLength,
244         data[0], data[1], data[2], data[3]);
245
246     //Sanity check
247     if(decodedLength != b64::b64_decode(data, encodedLength, NULL, NULL)){
248         printf("The decode will not be correct. Exiting\n");
249         return SOAP_ERR;
250     }
251     //Decode the data
252     char * decodedData = new char[decodedLength];
253     b64::b64_decode(data, encodedLength, decodedData, decodedLength);
254
255     printf("decodedData[0][1] = %c%c\n", decodedData[0], decodedData[1]);
256     //Open a file to write the decoded data to
257     HANDLE myHandle = CreateFileA(fileName, (GENERIC_READ | GENERIC_WRITE),
258                                     NULL, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
259     if(myHandle == INVALID_HANDLE_VALUE){
260         printf("CreateFile failed with %d\n", GetLastError());
261         return SOAP_ERR;
262     }
263
264     //Write the data
265     DWORD numWrote;
266     BOOL b = WriteFile(myHandle, decodedData, decodedLength, &numWrote, NULL);
267     if(b){
268         if(debug) printf("Wrote %d bytes of data to %s\n", numWrote, fileName);
269     }
270     CloseHandle(myHandle);
271     delete[] decodedData;
272
273     result = 1;
274
275     return SOAP_OK;
276
277 }
278
279 //Allows you to ask for a file and get it back in base64.
280 //NOTE: While I know it will be mitigated by the network architecture, features like this
281 //are why we should look into SOAP over SSL
282 int ns__receiveFileBase64(struct soap *soap, char * fileName, ns__receiveFileStruct &result){
283     int debug = 0;
284    
285     if(debug) printf("in ns__receiveFileBase64, about to open %s\n", fileName);
286
287     //Open the file
288     HANDLE myHandle = CreateFileA(fileName, GENERIC_READ, NULL, NULL,
289                                     OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
290     if(myHandle == INVALID_HANDLE_VALUE){
291         printf("couldn't open the file %s. Exiting\n", fileName);
292         return SOAP_ERR;
293     }
294
295     //Get the size and then read it into a buffer
296     unsigned int fileSize = (unsigned int)GetFileSize(myHandle, NULL);
297     if(fileSize <= 0){
298         printf("Error, or zero-length file\n");
299         return SOAP_ERR;
300     }
301     char * buffer = new char[fileSize];
302     memset(buffer, 0, fileSize);
303
304     DWORD numRead = 0;
305     BOOL b = ReadFile(myHandle, buffer, fileSize, &numRead,NULL);
306     if(!b || numRead != fileSize){
307         printf("ReadFile error\n");
308         return SOAP_ERR;
309     }
310     else{
311         if(debug) printf("Read the file successfully\n");
312     }
313
314     //base64 the file
315     unsigned int encodedLength = (unsigned int)b64::b64_encode(buffer, fileSize, NULL, NULL);
316     if(debug) printf("encodedLength = %d\n", encodedLength);
317     //TODO: make this a soap_malloc()
318     char * encodedData = new char[encodedLength];
319     memset(encodedData, 0, encodedLength);
320     size_t ret = b64::b64_encode(buffer, fileSize, encodedData, encodedLength);
321     if(ret == 0){
322         printf("size of the buffer was insufficient, or the length of the * converted buffer was longer than destLen\n");
323         return SOAP_ERR;
324     }
325
326     //return the file
327     result.data = encodedData;
328     result.encodedLength = encodedLength;
329     result.decodedLength = fileSize;
330
331     if(debug) printf("cleaning up\n");
332     CloseHandle(myHandle);
333     delete[] buffer;
334     //Don't delete[] encodedData because the SOAP stuff will need to grab the data from there for sending
335     //I'm just hoping that it deletes the memory and doesn't leak it
336     if(debug) printf("cleaned up successfully\n");
337
338     return SOAP_OK;
339 }
340
341 //After we have sent a document into the VM, we need a way to open it.
342 //We want to do this in a generic way, so rather than calling specific applications with the
343 //document as the parameter, we instead exploit the fact that as long as default handlers are
344 //set for a given file type, it can be run from the command line by simply typing its name.
345 //Thus we run cmd.exe with the /K option and the document name as a parameter.
346 //From cmd.exe help: "/K      Carries out the command specified by string but remains"
347
348 //fileName = absolute or relative file name you want to open
349 //waitTimeMillisec = milliseconds to Sleep() for. Should be unsigned int but SOAP::Lite doesn't
350 // have an 'unsigned int' type by default, and I didn't want to make one. Make sure you cast it to DWORD.
351 int ns__openDocument(struct soap *soap, char * fileName, int waitTimeMillisec, int &result){
352     int debug = 0;
353     if(debug) printf("in ns__openDocument, waitTimeMillisec = %d\n", waitTimeMillisec);
354
355     //Create the string for the parameters
356     wchar_t * docName = new wchar_t[1024];
357     wsprintf(docName, L"/K %hs", fileName);
358
359     //Create a job object to bind the processes I launch to
360     //This lets us get around the problem of Windows not having good ways of determining
361     //parent/child relationships, so that we don't have to care, and can for all intents
362     //and purposes, terminate the entire process tree starting with the cmd.exe we're launching
363     HANDLE myJobObj = CreateJobObject(NULL, NULL);
364     if(myJobObj == NULL){
365         printf("CreateJobObject failed with error %d\n", GetLastError());
366     }
367
368     //open with cmd.exe
369     STARTUPINFO myStart;
370     memset(&myStart, 0, sizeof(STARTUPINFO));
371     PROCESS_INFORMATION procInfo;
372     BOOL b = CreateProcess(L"C:\\WINDOWS\\system32\\cmd.exe", docName, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS,
373                             NULL, NULL, &myStart, &procInfo);
374     if(!b){
375         printf("CreateProcess failed with error %d\n", GetLastError());
376         return SOAP_ERR;
377     }
378    
379     //Add the process to the job object.
380     b = AssignProcessToJobObject(myJobObj, procInfo.hProcess);
381     if(!b){
382         printf("AssignProcessToJobObject failed with error %d\n", GetLastError());
383         return SOAP_ERR;
384     }
385
386     if(debug) printf("dwProcessId = %d, dwThreadId = %d\n", procInfo.dwProcessId, procInfo.dwThreadId);
387     if(debug) printf("Sleeping for %d seconds\n", (DWORD)waitTimeMillisec/1000);
388     Sleep((DWORD)waitTimeMillisec);
389     if(debug) printf("\n\nDone sleeping\n\n");
390    
391     //TODO: Before we terminate the jobs, see if it created any events. If so, let it run, and tell the
392     //HC Manager about it. The Manager should then request the information about events separately.
393     //This is the sort of check we would like for browsing as well
394
395     b = TerminateJobObject(myJobObj, 0);
396     if(!b){
397         printf("TerminateProcess failed with error %d\n", GetLastError());
398         return SOAP_ERR;
399     }
400     CloseHandle(procInfo.hProcess);
401     CloseHandle(procInfo.hThread);
402
403     result = 1;
404     return SOAP_OK;
405 }
406
407 //If maxEventsReturned == -1, then then send as many as possible.
408 int ns__returnRegistryEvents(struct soap *soap, int maxEventsToReturn, struct ns__dynRegArray **result){
409     char debug = 1;
410
411     struct ns__dynRegArray * dRegArray;
412     //Set up a dynamic array for each of the event types
413     dRegArray = soap_new_ns__dynRegArray(soap,1);
414     dRegArray->__ptr = NULL;
415     dRegArray->__size = regList.size();
416
417     //Figure out how many entries we will send back
418     if(maxEventsToReturn < dRegArray->__size && maxEventsToReturn != -1){
419         dRegArray->__size = maxEventsToReturn;
420     }
421     if(debug) printf("Sending back %d registy events\n",dRegArray->__size);
422
423     //Allocate a flat array to hold our ns__regkeys in
424     //TODO: see if soap_new_ns__regkey(soap, dRegArray->__size) works
425     struct ns__regkey * ns__regkeyArray = (struct ns__regkey *)soap_malloc(soap, dRegArray->__size*sizeof(struct ns__regkey));
426     dRegArray->__ptr = ns__regkeyArray;
427
428     for(unsigned int i = 0; i < dRegArray->__size; i++){
429         if(debug){
430             printf("i = %d\n", i);
431 //          printf("regList.front().time %s, %#x\n", regList.front().time, regList.front().time);
432 //          printf("regList.front().eventType %s, %#x\n", regList.front().eventType, regList.front().eventType);
433 //          printf("regList.front().procPID %d, %#x\n", regList.front().procPID, regList.front().procPID);
434 //          printf("regList.front().procName %s, %#x\n", regList.front().procName, regList.front().procName);
435             int * b = (int *)&regList.front();
436             for(int i = 0; i < 8; i++){
437                 printf("regEvent[%d] = %#x\n", i, b[i]);
438             }
439         }
440         memcpy(&ns__regkeyArray[i],&regList.front(), sizeof(struct ns__regkey));
441         regDeallocList.push_back(regList.front()); //Need to keep track of it to dealloc its elements later
442         regList.pop_front();
443     }
444     *result = dRegArray;
445     return SOAP_OK;
446 }
447 int ns__returnFileEvents(struct soap *soap, int maxEventsToReturn, struct ns__dynFileArray **result){
448     char debug = 1;
449
450     struct ns__dynFileArray * dFileArray;
451     dFileArray = soap_new_ns__dynFileArray(soap,1);
452     dFileArray->__ptr = NULL;
453     dFileArray->__size = fileList.size();
454     printf("SIZE= %d",dFileArray->__size);
455     printf("MAX= %d",maxEventsToReturn);
456
457     //Figure out how many entries we will send back
458     if(maxEventsToReturn < dFileArray->__size && maxEventsToReturn != -1){
459         dFileArray->__size = maxEventsToReturn;
460     }
461     if(debug) printf("Sending back %d file events\n",dFileArray->__size);
462
463     struct ns__processFile * ns__processFileArray = (struct ns__processFile *)soap_malloc(soap, dFileArray->__size*sizeof(struct ns__processFile));
464     dFileArray->__ptr = ns__processFileArray;
465
466     for(unsigned int i = 0; i < dFileArray->__size; i++){
467         memcpy(&ns__processFileArray[i],&fileList.front(), sizeof(struct ns__processFile));
468         fileDeallocList.push_back(fileList.front());
469         fileList.pop_front();
470     }
471    
472     *result = dFileArray;
473     return SOAP_OK;
474 }
475 int ns__returnProcessEvents(struct soap *soap, int maxEventsToReturn, struct ns__dynProcArray **result){
476     char debug = 1;
477
478     struct ns__dynProcArray * dProcArray;
479     dProcArray = soap_new_ns__dynProcArray(soap,1);
480     dProcArray->__ptr = NULL;
481     dProcArray->__size = procList.size();
482
483     if(maxEventsToReturn < dProcArray->__size && maxEventsToReturn != -1){
484         dProcArray->__size = maxEventsToReturn;
485     }
486     if(debug) printf("Sending back %d process events\n",dProcArray->__size);
487
488     struct ns__osProcess * ns__osProcessArray = (struct ns__osProcess *)soap_malloc(soap, dProcArray->__size*sizeof(struct ns__osProcess));
489     dProcArray->__ptr = ns__osProcessArray;
490
491     for(unsigned int i = 0; i < dProcArray->__size; i++){
492         memcpy(&ns__osProcessArray[i],&procList.front(), sizeof(struct ns__osProcess));
493         procDeallocList.push_back(procList.front());
494         procList.pop_front();
495     }
496    
497     *result = dProcArray;
498     return SOAP_OK;
499 }
500 int ns__returnEvents(struct soap *soap, int maxEventsToReturn, ns__allEvents **result){
501     char debug = 1;
502
503     ns__allEvents * all = soap_new_ns__allEvents(soap, 1);
504     all->regkeys = NULL;
505     all->process_files = NULL;
506     all->os_processes = NULL;
507     if(regList.empty() || maxEventsToReturn == 0){
508         printf("No registry events to send back\n");
509     }
510     else{
511         ns__returnRegistryEvents(soap,maxEventsToReturn,&all->regkeys);
512     }
513     if(fileList.empty() || maxEventsToReturn == 0){
514         printf("No file events to send back\n");
515     }
516     else{
517         ns__returnFileEvents(soap,maxEventsToReturn,&all->process_files);
518     }
519     if(procList.empty() || maxEventsToReturn == 0){
520         printf("No process events to send back\n");
521     }
522     else{
523         ns__returnProcessEvents(soap,maxEventsToReturn,&all->os_processes);
524     }
525
526     *result = all;
527     if(debug) printf("all->regkeys = %#x, all->process_files = %#x, all->os_processes = %#x\n", all->regkeys, all->process_files, all->os_processes);
528     if(debug) printf("regList.size() = %d, fileList.size() = %d, procList.size() = %d\n", regList.size(), fileList.size(), procList.size());
529
530     return SOAP_OK;
531 }
532
533 //Helper function to deallocate any memory in events which have already had their data sent via SOAP
534 void dealloc_events(){
535
536     if(!regDeallocList.empty()){
537         for(int i = 0; i < regDeallocList.size(); i++){
538             free(regDeallocList.front().created_at);
539             free(regDeallocList.front().event_type);
540             free(regDeallocList.front().proc_name);
541             free(regDeallocList.front().name);
542             free(regDeallocList.front().value_name);
543             free(regDeallocList.front().value_type);
544             free(regDeallocList.front().value);
545             regDeallocList.pop_front();
546         }
547     }
548
549     if(!fileDeallocList.empty()){
550         for(int i = 0; i < fileDeallocList.size(); i++){
551             free(fileDeallocList.front().created_at);
552             free(fileDeallocList.front().event_type);
553             free(fileDeallocList.front().proc_name);
554             free(fileDeallocList.front().name);
555             fileDeallocList.pop_front();
556         }
557     }
558
559     if(!procDeallocList.empty()){
560         for(int i = 0; i < procDeallocList.size(); i++){
561             free(procDeallocList.front().created_at);
562             free(procDeallocList.front().event_type);
563             free(procDeallocList.front().parent_name);
564             free(procDeallocList.front().name);
565             procDeallocList.pop_front();
566         }
567     }
568
569 }
570
571 //Thus far, SOAP::Lite hasn't been sending the data correctly, so we never get into this function.
572 //Removing the "This is a multipart message in MIME format..." from MIME::Entity's Entity.pm at least gets
573 //rid of the gSOAP "No XML element" error, but then it doesn't seem to ever exit the SOAP::Lite send.
574 int ns__sendMIME(struct soap *soap, int magicNumber, int &result){
575     printf("In ns__sendMIME\n");
576
577     //From the gSOAP documentation example
578     struct soap_multipart * attachment;
579     for(attachment = soap->mime.list; attachment; attachment = attachment->next){
580        printf("MIME attachment:\n");
581        printf("Memory=%p\n", (*attachment).ptr);
582        printf("Size=%ul\n", (*attachment).size);
583        printf("Encoding=%d\n", (int)(*attachment).encoding);
584        printf("Type=%s\n", (*attachment).type?(*attachment).type:"null");
585        printf("ID=%s\n", (*attachment).id?(*attachment).id:"null");
586        printf("Location=%s\n", (*attachment).location?(*attachment).location:"null");
587        printf("Description=%s\n", (*attachment).description?(*attachment).description:"null");
588     }
589
590     printf("magicNumber = %#x\n", magicNumber);
591     if(magicNumber == 123){
592         result = 42;
593     }
594     else{
595         result = 0;
596     }
597
598     return SOAP_OK;
599 }
Note: See TracBrowser for help on using the browser.