root/trunk/uploadr/MacUploadr.app/Contents/Resources/components/flGM.cpp

Revision 585, 35.2 kB (checked in by jdecq, 7 months ago)

=build fix

Line 
1 /*
2 * Flickr Uploadr
3 *
4 * Copyright (c) 2007-2008 Yahoo! Inc.  All rights reserved.  This library is
5 * free software; you can redistribute it and/or modify it under the terms of
6 * the GNU General Public License (GPL), version 2 only.  This library is
7 * distributed WITHOUT ANY WARRANTY, whether express or implied. See the GNU
8 * GPL for more details (http://www.gnu.org/licenses/gpl.html)
9 */
10
11 #include <stdio.h>
12
13 // link hack for MinGW
14 #ifdef WIN32
15
16 #include <string.h>
17 extern "C" __declspec(dllexport) __checkReturn int     __cdecl strcasecmp(__in_z  const char * _Str1, __in_z  const char * _Str2) {
18         return _stricmp(_Str1, _Str2);
19 }
20 // For upload
21 #include "afxinet.h"
22 //#include <Windows.h>
23 const DWORD dwChunckSize = 64 * 1024;
24 extern "C" __declspec(dllimport) UINT ___lc_codepage_func(void);
25
26 extern "C" unsigned int __lc_codepage = ___lc_codepage_func(); //I can't link without that !?????????????????
27
28
29 #endif
30
31 #include "flGM.h"
32
33 // GraphicsMagick
34 #include "Magick++.h"
35
36 // Exiv2
37 #include "image.hpp"
38 #include "exif.hpp"
39 #include "iptc.hpp"
40
41 // Goofy FFmpeg requires C linkage
42 extern "C" {
43 #include <libavcodec/avcodec.h>
44 #include <libavformat/avformat.h>
45 #include <libswscale/swscale.h>
46 }
47
48 #include <stdlib.h>
49 #include <sstream>
50 #include <string>
51 #include <cctype>       // std::tolower
52 #include <sys/stat.h>
53 #include "nsCOMPtr.h"
54 #include "nsComponentManagerUtils.h"
55 #include "nsILocalFile.h"
56 #include "nsDirectoryServiceUtils.h"
57 #include "nsEmbedString.h"
58
59 // _NSGetExecutablePath on Macs
60 #ifdef XP_MACOSX
61 #include <mach-o/dyld.h>
62 #endif
63
64 // GetShortPathName, GetWindowsDirectory and CopyFile on Windows
65 #ifdef XP_WIN
66 #include <windows.h>
67 #endif
68
69 #define round(n) (int)(0 <= (n) ? (n) + 0.5 : (n) - 0.5)
70 static int sws_flags = SWS_BICUBIC;
71
72 using namespace std;
73
74 static volatile bool gbCancel = false;
75
76 #ifdef WIN32
77 CString GenerateHeader(const CString& version, const CString& boundary) {
78         CString res;
79         CString temp = _T("User-Agent: Flickr Uploadr %s\r\nContent-Type: multipart/form-data; boundary=%s\r\n\r\n");
80         res.Format(temp, version, boundary);
81         return res;
82 }
83 #endif
84
85 // Prototypes
86 string * conv_path(const nsAString &, bool);
87 string * find_path(string *, const char *);
88 int base_orient(Exiv2::ExifData &, Magick::Image &);
89 bool exif_get_last_value_of_key(const Exiv2::ExifData & exif, const string & sKey, long & value);
90 void exif_update_dim(Exiv2::ExifData &, int, int);
91 void unconv_path(string &, nsAString &);
92
93 // Convert a path from a UTF-16 nsAString to an ASCII std::string
94 //   In Windows, this will handle all the Unicode weirdness paths come with
95 //   If is_dir is true then the returned path will transparently become an
96 //   ASCII-safe path, possibly to a TEMP dir
97 //   If is_dir is false then the path will be made safe or the file will be
98 //   copied under a new name to an ASCII-safe TEMP dir
99 string * conv_path(const nsAString & utf16, bool is_dir) {
100
101         // Fun with Windows paths
102 #ifdef XP_WIN
103
104         // Is this path outside of ASCII?
105         PRUnichar * utf16_start = (PRUnichar *)utf16.BeginReading();
106         const PRUnichar * utf16_end = (const PRUnichar *)utf16.EndReading();
107         bool needs_unicode = false;
108         while (utf16_start != utf16_end) {
109                 if (0x7f < *utf16_start++) {
110                         needs_unicode = true;
111                         break;
112                 }
113         }
114
115         // We're outside of ASCII so we need help
116         if (needs_unicode) {
117
118                 // UTF-16 nsAString to wchar_t[]
119                 wchar_t * wide_arr = new wchar_t[utf16.Length() + 1];
120                 if (0 == wide_arr) return 0;
121                 wchar_t * wide_arr_p = wide_arr;
122                 PRUnichar * wide_start = (PRUnichar *)utf16.BeginReading();
123                 const PRUnichar * wide_end = (const PRUnichar *)utf16.EndReading();
124                 while (wide_start != wide_end) {
125                         *wide_arr_p++ = (wchar_t)*wide_start++;
126                 }
127                 *wide_arr_p = 0;
128
129                 // Try GetShortPathNameW to get ASCII in a wchar_t *
130                 wchar_t short_arr[4096];
131                 *short_arr = 0;
132                 int sp = GetShortPathNameW(wide_arr, short_arr, 4096);
133
134                 // See if we still need Unicode
135                 needs_unicode = false;
136                 wchar_t * short_arr_p = short_arr;
137                 while (*short_arr_p) {
138                         if (0x7f < *short_arr_p++) {
139                                 needs_unicode = true;
140                                 break;
141                         }
142                 }
143
144                 if (0 == sp || needs_unicode) {
145
146                         // Try to find a TEMP directory
147                         //   This would be the easy way except that it will never work
148                         //   for users with Unicode characters in their usernames
149                         /*
150                         char temp_arr[4096];
151                         *temp_arr = 0;
152                         if (0 == GetTempPathA(4096, temp_arr)) {
153                         delete [] wide_arr;
154                         return 0;
155                         }
156                         */
157
158                         // Try to find a TEMP directory
159                         //   (This is the hard way but at least it will work)
160                         //   Get the drive letter from the Windows directory and append
161                         //   :\temp, create that directory and use it for TEMP
162                         char win_arr[4096];
163                         *win_arr = 0;
164                         if (0 == GetWindowsDirectoryA(win_arr, 4096)) {
165                                 delete [] wide_arr;
166                                 return 0;
167                         }
168                         char temp_arr[9];
169                         temp_arr[0] = *win_arr;
170                         temp_arr[1] = ':'; temp_arr[2] = '\\';
171                         temp_arr[3] = 't'; temp_arr[4] = 'e';
172                         temp_arr[5] = 'm'; temp_arr[6] = 'p';
173                         temp_arr[7] = '\\'; temp_arr[8] = 0;
174                         CreateDirectoryA(temp_arr, 0);
175
176                         // Directory requests can just have the TEMP directory
177                         if (is_dir) {
178                                 delete [] wide_arr;
179                                 return new string(temp_arr);
180                         }
181
182                         // But if this is a file we actually need to copy it
183                         string base(temp_arr);
184                         base += "original";
185
186                         // Copy the file extension of the original to our base
187                         wstring wide_w(wide_arr);
188                         wstring ext_w = wide_w.substr(wide_w.rfind('.'));
189                         string ext_s;
190                         wchar_t * ext_p = (wchar_t *)ext_w.c_str();
191                         while (*ext_p) {
192                                 ext_s += (char)*ext_p++;
193                         }
194                         base += ext_s;
195
196                         // Destination path
197                         string * temp = find_path(&base, "");
198                         wchar_t * temp_wide_arr = new wchar_t[temp->size() + 1];
199                         wchar_t * temp_wide_arr_p = temp_wide_arr;
200                         char * temp_p = (char *)temp->c_str();
201                         while (*temp_p) {
202                                 *temp_wide_arr_p++ = (wchar_t)*temp_p++;
203                         }
204                         *temp_wide_arr_p = 0;
205
206                         // Copy the file
207                         if (0 == CopyFileW(wide_arr, temp_wide_arr, false)) {
208                                 delete [] wide_arr;
209                                 delete temp;
210                                 delete [] temp_wide_arr;
211                                 return 0;
212                         }
213                         delete [] wide_arr;
214                         delete [] temp_wide_arr;
215                         return temp;
216
217                 }
218                 delete [] wide_arr;
219
220                 // GetShortPathNameW was successful!
221                 // wchar_t * to std::string
222                 string * short_s = new string();
223                 if (0 == short_s) return 0;
224                 short_arr_p = short_arr;
225                 while (*short_arr_p) {
226                         *short_s += (char)*short_arr_p++;
227                 }
228                 return short_s;
229
230         }
231
232         // Within ASCII, pack it down
233         else {
234                 char * ascii_arr = new char[utf16.Length() + 1];
235                 if (0 == ascii_arr) return 0;
236                 char * ascii_arr_p = ascii_arr;
237                 utf16_start = (PRUnichar *)utf16.BeginReading();
238                 utf16_end = (const PRUnichar *)utf16.EndReading();
239                 while (utf16_start != utf16_end) {
240                         *ascii_arr_p++ = (char)*utf16_start++;
241                 }
242                 *ascii_arr_p = 0;
243                 string * ascii_s = new string(ascii_arr);
244                 delete [] ascii_arr;
245                 return ascii_s;
246         }
247
248         // Macs just need UTF-8
249 #else
250
251         // UTF-16 nsAString to UTF-8 nsCString
252         nsCString utf8 = NS_ConvertUTF16toUTF8(utf16);
253
254         // UTF-8 nsCString to std::string
255         char * utf8_arr = new char[utf8.Length() + 1];
256         if (0 == utf8_arr) return 0;
257         char * utf8_arr_p = utf8_arr;
258         char * utf8_start = (char *)utf8.BeginReading();
259         const char * utf8_end = (const char *)utf8.EndReading();
260         while (utf8_start != utf8_end) {
261                 *utf8_arr_p++ = *utf8_start++;
262         }
263         *utf8_arr_p = 0;
264         string * utf8_s = new string(utf8_arr);
265         delete [] utf8_arr;
266         return utf8_s;
267
268 #endif
269
270 }
271
272 // Find a path for the new image file in our profile
273 //   If extra is empty, the new path will be in the same directory,
274 //   otherwise it will be in the Profile
275 string * find_path(string * path_s, const char * extra) {
276         if (0 == path_s || 0 == extra) {
277                 return 0;
278         }
279         string * dir_s = 0;
280         if (*extra) {
281                 nsCOMPtr<nsIFile> dir_ptr;
282                 nsresult nsr = NS_GetSpecialDirectory("ProfD", getter_AddRefs(dir_ptr));
283                 if (NS_FAILED(nsr)) {
284                         return 0;
285                 }
286                 dir_ptr->AppendNative(NS_LITERAL_CSTRING("images"));
287                 PRBool dir_exists = PR_FALSE;
288                 dir_ptr->Exists(&dir_exists);
289                 if (!dir_exists) {
290                         dir_ptr->Create(nsIFile::DIRECTORY_TYPE, 0770);
291                 }
292                 nsEmbedString dir;
293                 dir_ptr->GetPath(dir);
294                 dir_s = conv_path(dir, true);
295                 if (0 == dir_s) {
296                         return 0;
297                 }
298 #ifdef XP_WIN
299                 dir_s->append(path_s->substr(path_s->rfind('\\')));
300 #else
301                 dir_s->append(path_s->substr(path_s->rfind('/')));
302 #endif
303                 size_t period = dir_s->rfind('.');
304                 dir_s->insert(period, extra);
305         } else {
306                 dir_s = new string(*path_s);
307                 if (0 == dir_s) {
308                         return 0;
309                 }
310         }
311         ostringstream index;
312         string dir_s_save(*dir_s);
313         int i = 0;
314         struct stat st;
315         while (0 == stat(dir_s->c_str(), &st)) {
316                 index.str("");
317                 index << ++i;
318                 *dir_s = dir_s_save;
319                 dir_s->insert(dir_s->rfind('.'), index.str());
320         }
321         return dir_s;
322 }
323
324 // Orient an image's pixels as EXIF instructs
325 int base_orient(Exiv2::ExifData & exif, Magick::Image & img) {
326         long orient = -1;
327
328         try {
329                 std::stable_sort(exif.begin(), exif.end(), Exiv2::cmpMetadataByKey); // this is the same as ExifData::sortByKey(), but stable
330                 exif_get_last_value_of_key(exif, "Exif.Image.Orientation", orient) ||
331                         exif_get_last_value_of_key(exif, "Exif.Panasonic.Rotation", orient) ||
332                         exif_get_last_value_of_key(exif, "Exif.MinoltaCs5D.Rotation", orient);
333         } catch (Exiv2::Error & e) {
334                 cerr << e.what();
335         }
336         if (1 > orient || 8 < orient) {
337                 orient = 1;
338         }
339         switch (orient) {
340                 case 2:
341                         img.flop();
342                         break;
343                 case 3:
344                         img.rotate(180.0);
345                         break;
346                 case 4:
347                         img.flip();
348                         break;
349                 case 5:
350                         img.rotate(90.0);
351                         img.flip();
352                         break;
353                 case 6:
354                         img.rotate(90.0);
355                         break;
356                 case 7:
357                         img.rotate(270.0);
358                         img.flip();
359                         break;
360                 case 8:
361                         img.rotate(270.0);
362                         break;
363                 default:
364                         break;
365         }
366         return orient;
367 }
368
369 // returns true if the key is present and value is the "last" value found
370 // hack for http://cvs.flickr.com/b2/4584
371 bool exif_get_last_value_of_key(const Exiv2::ExifData & exif, const string & sKey, long & value) {
372         Exiv2::ExifData::const_iterator it = std::find_if(exif.begin(), exif.end(),
373                 Exiv2::FindMetadatumByKey(sKey)); // same as ExifData::findKey but with specific start point
374         if(it == exif.end())
375                 return false;
376         while(it != exif.end()) {
377                 value = it->toLong();
378                 it = std::find_if(it+1, exif.end(), Exiv2::FindMetadatumByKey(sKey));
379         }
380         return true;
381 }
382 // Update the image width and height in EXIF
383 void exif_update_dim(Exiv2::ExifData & exif, int w, int h) {
384
385         // Keys to search
386         char * keys[2][4] = {
387
388                 // Width
389                 {
390                         "Exif.Iop.RelatedImageWidth",
391                                 "Exif.Photo.PixelXDimension", // Only for compressed images
392                                 "Exif.Image.ImageWidth", // From TIFF-land
393                                 0
394                 },
395
396                 // Height
397                 {
398                         "Exif.Iop.RelatedImageLength",
399                                 "Exif.Photo.PixelYDimension", // Only for compressed images
400                                 "Exif.Image.ImageLength", // From TIFF-land
401                                 0
402                         }
403
404         };
405
406         // Once for width, once for height
407         int values[2] = { w, h };
408         for (unsigned int i = 0; i < 2; ++i) {
409
410                 // For each key we have, if it's set, override it
411                 unsigned int j = 0;
412                 unsigned int done = 0;
413                 while (0 != keys[i][j]) {
414                         string str = string(keys[i][j]);
415                         Exiv2::ExifKey key = Exiv2::ExifKey(str);
416                         Exiv2::ExifData::iterator it = exif.findKey(key);
417                         if (exif.end() != it) {
418                                 exif[keys[i][j]] = uint32_t(values[i]);
419                                 done = 1;
420                         }
421                         ++j;
422                 }
423
424                 // As a last resort, set the TIFF tags
425                 if (!done) {
426                         exif[keys[i][0]] = uint32_t(values[i]);
427                 }
428
429         }
430 }
431
432 // Get a path/string ready to send back to JavaScript-land
433 //   On Windows there is little to do, but Macs must go from UTF-8 to UTF-16
434 void unconv_path(string & path_s, nsAString & _retval) {
435         size_t pos = path_s.find("###", 0);
436         while (string::npos != pos) {
437                 path_s.replace(pos, 3, "{---THREE---POUND---DELIM---}");
438                 pos = path_s.find("###", pos);
439         }
440         char * o = (char *)path_s.c_str();
441 #ifdef XP_MACOSX
442         nsCString utf8;
443 #endif
444         while (*o) {
445
446                 // Macs will still have UTF-8 at this point
447 #ifdef XP_MACOSX
448                 utf8.Append(*o++);
449
450                 // Windows is good to go, being ASCII and all
451 #else
452                 _retval.Append(*o++);
453
454 #endif
455         }
456
457         // Finish up the Mac transform to UTF-16
458 #ifdef XP_MACOSX
459         _retval.Append(NS_ConvertUTF8toUTF16(utf8));
460 #endif
461
462 }
463
464 void quote(string & s) {
465         if (string::npos != s.find(" ", 0)) {
466                 s.insert(0, "\"").append("\"");
467         }
468 }
469
470 // cf. http://www.metadataworkinggroup.com/pdf/mwg_guidance.pdf p.30 for non-XMP metadata container open encoding
471 void AttemptUTF8ASCIIGuess(const string & s, nsAString & ns) {
472         ns = NS_ConvertUTF8toUTF16(s.c_str());
473         if(ns.IsEmpty())
474                 ns = NS_ConvertASCIItoUTF16(s.c_str());
475 }
476
477 // Extract a metadata key if it exists in the collection given
478 template<class D, class K>
479 bool extract(D & data, const char * k, nsAString & ns, bool q) {
480         typename D::iterator it = data.findKey(K(string(k)));
481         if (data.end() == it) { return false; }
482         else {
483                 string s = it->toString();
484                 if (q) { quote(s); }
485                 AttemptUTF8ASCIIGuess(s, ns);           
486                 return true;
487         }
488 }
489
490
491 NS_IMPL_ISUPPORTS1(flGM, flIGM)
492
493 flGM::flGM() : m_Observer(0) {
494 }
495
496 flGM::~flGM() {
497 }
498
499 // Initialize our GraphicsMagick/ffmpeg setup
500 NS_IMETHODIMP flGM::Init(const nsAString & pwd, UploadObserver *observer)
501 {
502         m_Observer = observer;
503
504         //Mac needs to setup GraphicsMagick
505 #ifdef XP_MACOSX
506         char path[1024];
507         unsigned int size = 1024;
508         _NSGetExecutablePath(&path[0], &size);
509         Magick::InitializeMagick(&path[0]);
510 #endif
511
512         // Windows needs to get its working directory ready for GraphicsMagick
513 #ifdef XP_WIN
514         string * pwd_s = conv_path(pwd, true);
515         if (0 == pwd_s) return NS_ERROR_NULL_POINTER;
516         SetCurrentDirectoryA(pwd_s->c_str());
517         delete pwd_s;
518 #endif
519
520         // Register all video codecs
521         av_register_all();
522
523         MagickLib::ExceptionInfo exception;
524         const MagickLib::MagickInfo *info;
525         GetExceptionInfo(&exception);
526         info = GetMagickInfo(NULL, &exception);
527
528
529         return NS_OK;
530 }
531
532 // Create a thumbnail of the image, preserving aspect ratio and store it to the profile
533 NS_IMETHODIMP flGM::Thumb(PRInt32 square, const nsAString & path, nsAString & _retval) {
534         string * path_s = 0;
535         string * thumb_s = 0;
536         try {
537
538                 // Get the path as a C++ string
539                 path_s = conv_path(path, false);
540                 if (!path_s) { return NS_ERROR_INVALID_ARG; }
541
542                 // Open the image
543                 Magick::Image img(*path_s);
544
545                 // Extract EXIF and IPTC data that we care about
546                 int orient = 1;
547                 nsString title, description, date_taken;
548                 nsString tags;
549                 try {
550                         Exiv2::Image::AutoPtr meta_r = Exiv2::ImageFactory::open(*path_s);
551                         meta_r->readMetadata();
552
553                         // EXIF orientation
554                         Exiv2::ExifData & exif = meta_r->exifData();
555                         orient = base_orient(exif, img);
556
557                         // XMP and IPTC metadata
558                         Exiv2::XmpData & xmp = meta_r->xmpData();
559                         Exiv2::IptcData & iptc = meta_r->iptcData();
560                         extract<Exiv2::XmpData, Exiv2::XmpKey>(
561                                 xmp, "Xmp.dc.title", title, false)
562                                 || extract<Exiv2::IptcData, Exiv2::IptcKey>(
563                                 iptc, "Iptc.Application2.ObjectName", title, false)
564                                 || extract<Exiv2::IptcData, Exiv2::IptcKey>(
565                                 iptc, "Iptc.Application2.Headline", title, false);
566                         extract<Exiv2::XmpData, Exiv2::XmpKey>(
567                                 xmp, "Xmp.dc.description", description, false)
568                                 || extract<Exiv2::XmpData, Exiv2::XmpKey>(
569                                 xmp, "Xmp.photoshop.Headline", description, false)
570                                 || extract<Exiv2::IptcData, Exiv2::IptcKey>(
571                                 iptc, "Iptc.Application2.Caption", description, false)
572                                 || extract<Exiv2::ExifData, Exiv2::ExifKey>(
573                                 exif, "Exif.Image.ImageDescription", description, false);
574                         string key("Iptc.Application2.Keywords");
575                         Exiv2::IptcKey k = Exiv2::IptcKey(key);
576                         Exiv2::IptcData::iterator i, ii = iptc.end();
577                         nsString tmpString;
578                         for (i = iptc.begin(); i != ii; ++i) {
579                                 if (i->key() == key) {
580                                         string val = i->toString();
581                                         quote(val);
582                                         AttemptUTF8ASCIIGuess(val, tmpString);
583                                         tags.Append(tmpString);
584                                         tags.Append(NS_LITERAL_STRING(" ").get());
585                                 }
586                         }
587                         nsString city, state, country;
588                         extract<Exiv2::IptcData, Exiv2::IptcKey>(
589                                 iptc, "Iptc.Application2.City", city, true);
590                         tags.Append(city);
591                         tags.Append(NS_LITERAL_STRING(" ").get());
592                         extract<Exiv2::IptcData, Exiv2::IptcKey>(
593                                 iptc, "Iptc.Application2.ProvinceState", state, true);
594                         tags.Append(state);
595                         tags.Append(NS_LITERAL_STRING(" ").get());
596                         extract<Exiv2::IptcData, Exiv2::IptcKey>(
597                                 iptc, "Iptc.Application2.CountryName", country, true);
598                         tags.Append(country);
599
600                         // XMP and EXIF date taken
601                         extract<Exiv2::XmpData, Exiv2::XmpKey>(
602                                 xmp, "Xmp.exif.DateTimeOriginal", date_taken, false)
603                                 || extract<Exiv2::XmpData, Exiv2::XmpKey>(
604                                 xmp, "Xmp.exif.DateTimeDigitized", date_taken, false)
605                                 || extract<Exiv2::XmpData, Exiv2::XmpKey>(
606                                 xmp, "Xmp.iptc.DateTime", date_taken, false)
607                                 || extract<Exiv2::ExifData, Exiv2::ExifKey>(            // Previously
608                                 exif, "Exif.Photo.DateTimeOriginal", date_taken, false) // this was primary
609                                 || extract<Exiv2::ExifData, Exiv2::ExifKey>(
610                                 exif, "Exif.Photo.DateTimeDigitized", date_taken, false)
611                                 || extract<Exiv2::ExifData, Exiv2::ExifKey>(
612                                 exif, "Exif.Image.DateTime", date_taken, false)
613                                 || extract<Exiv2::ExifData, Exiv2::ExifKey>(
614                                 exif, "Exif.Photo.DateTime", date_taken, false)
615                                 || extract<Exiv2::ExifData, Exiv2::ExifKey>(
616                                 exif, "Xmp.exif.DateTime", date_taken, false)
617                                 || extract<Exiv2::ExifData, Exiv2::ExifKey>(
618                                 exif, "Xmp.photoshop.DateCreate", date_taken, false)
619                                 ;
620
621
622                 } catch (Exiv2::Error &) {}
623
624                 ostringstream out1;
625                 out1 << orient << "###";
626
627                 // Original size
628                 int bw, bh;
629                 if (5 > orient) {
630                         bw = img.baseColumns();
631                         bh = img.baseRows();
632                 } else {
633                         bw = img.baseRows();
634                         bh = img.baseColumns();
635                 }
636                 int base = bw > bh ? bw : bh;
637                 out1 << bw << "###" << bh << "###";
638
639
640                 // Thumbnail width and height
641                 ostringstream dim;
642                 ostringstream outTemp;
643                 if (bw > bh) {
644                         float r = (float)bh * (float)square / (float)bw;
645                         outTemp << square << "###" << round(r);
646                         dim << square << "x" << round(r);
647                 } else {
648                         float r = (float)bw * (float)square / (float)bh;
649                         outTemp << round(r) << "###" << square;
650                         dim << round(r) << "x" << square;
651                 }
652                 outTemp << "###";
653
654                 // Hide ### strings within the IPTC data
655                 PRInt32 pos = title.Find("###", 0);
656                 while (string::npos != pos) {
657                         title.Replace(pos, 3, NS_LITERAL_STRING("{---THREE---POUND---DELIM---}"));
658                         pos = title.Find("###", pos);
659                 }
660                 pos = description.Find("###", 0);
661                 while (string::npos != pos) {
662                         description.Replace(pos, 3, NS_LITERAL_STRING("{---THREE---POUND---DELIM---}"));
663                         pos = description.Find("###", pos);
664                 }
665                 pos = tags.Find("###", 0);
666                 while (string::npos != pos) {
667                         tags.Replace(pos, 3, NS_LITERAL_STRING("{---THREE---POUND---DELIM---}").get());
668                         pos = tags.Find("###", pos);
669                 }
670
671                 // Create a new path
672                 thumb_s = find_path(path_s, "-thumb");
673                 if (!thumb_s) { return NS_ERROR_NULL_POINTER; }
674                 delete path_s; path_s = 0;
675
676                 // If this image is a TIFF, force the thumbnail to be a JPEG
677                 std::string thumbLowerCase(*thumb_s);
678                 std::transform(thumb_s->begin(), thumb_s->end(), thumbLowerCase.begin(), (int(*)(int))tolower);
679                 if (thumbLowerCase.rfind(".tif") + 6 > thumbLowerCase.length() ||
680                         thumbLowerCase.rfind(".bmp") + 6 > thumbLowerCase.length()) {
681                                 thumb_s->append(".jpg");
682                 }
683
684                 // Find the sharpen sigma as the website does
685                 double sigma;
686                 if (base <= 800) { sigma = 1.9; }
687                 else if (base <= 1600) { sigma = 2.85; }
688                 else { sigma = 3.8; }
689
690                 // Create the actual thumbnail
691                 img.scale(dim.str());
692                 img.sharpen(1, sigma);
693                 img.compressType(Magick::NoCompression);
694                 img.write(*thumb_s);
695
696                 // If all went well, return stuff
697                 //<orient>###<width>###<height>
698                 _retval.Append(NS_ConvertUTF8toUTF16(out1.str().c_str()));
699                 //###<date_taken>###
700                 _retval.Append(date_taken);
701                 _retval.Append(NS_LITERAL_STRING("###"));
702                 //<thumb_width>###<thumb_height>###
703                 _retval.Append(NS_ConvertUTF8toUTF16(outTemp.str().c_str()));
704                 //<thumb_path>
705                 unconv_path(*thumb_s, _retval);
706                 delete thumb_s; thumb_s = 0;
707                 //###<title>###<description>###<tags>                           
708                 _retval.Append(NS_LITERAL_STRING("###"));
709                 _retval.Append(title);
710                 _retval.Append(NS_LITERAL_STRING("###"));
711                 _retval.Append(description);
712                 _retval.Append(NS_LITERAL_STRING("###"));
713                 _retval.Append(tags);
714
715                 //debug
716                 /*              nsString nsTestString(_retval);
717                 basic_string<PRUnichar, char_traits<PRUnichar>, allocator<PRUnichar> > blabla = nsTestString.get();
718                 */
719                 return NS_OK;
720         }
721
722         // Otherwise yell about it
723         catch (Magick::Exception & e) {
724                 if(path_s) {
725                         delete path_s;
726                         path_s = NULL;
727                 }
728                 if(thumb_s) {
729                         delete thumb_s;
730                         thumb_s = NULL;
731                 }
732                 char * o = (char *)e.what();
733                 while (*o) { _retval.Append(*o++); }
734         }
735         return NS_OK;
736
737 }
738
739 // Rotate an image, preserving size and store it to the profile
740 NS_IMETHODIMP flGM::Rotate(PRInt32 degrees, const nsAString & path, nsAString & _retval) {
741         string * path_s = 0;
742         string * rotate_s = 0;
743         try {
744
745                 // Don't rotate 0 degrees
746                 if (0 == degrees) {
747                         _retval.Append('o');
748                         _retval.Append('k');
749                         return NS_OK;
750                 }
751
752                 path_s = conv_path(path, false);
753                 if (!path_s) { return NS_ERROR_INVALID_ARG; }
754
755                 // Yank out all the metadata we want to save
756                 Exiv2::ExifData exif;
757                 Exiv2::IptcData iptc;
758                 Exiv2::XmpData xmp;
759                 try {
760                         Exiv2::Image::AutoPtr meta_r = Exiv2::ImageFactory::open(*path_s);
761                         meta_r->readMetadata();
762                         exif = meta_r->exifData();
763                         iptc = meta_r->iptcData();
764                         xmp = meta_r->xmpData();
765                 } catch (Exiv2::Error &) {}
766
767                 // Create a new path
768                 rotate_s = find_path(path_s, "-rotate");
769                 if (!rotate_s) { return NS_ERROR_NULL_POINTER; }
770
771                 // Rotate the image
772                 Magick::Image img(*path_s);
773                 delete path_s; path_s = 0;
774                 base_orient(exif, img);
775                 img.rotate(degrees);
776                 img.compressType(Magick::NoCompression);
777                 img.write(*rotate_s);
778
779                 // Set the orientation to 1 because we're orienting the pixels manually
780                 exif["Exif.Image.Orientation"] = uint32_t(1);
781
782                 // Put saved metadata into the resized image
783                 try {
784                         Exiv2::Image::AutoPtr meta_w = Exiv2::ImageFactory::open(*rotate_s);
785                         meta_w->setExifData(exif);
786                         meta_w->setIptcData(iptc);
787                         meta_w->setXmpData(xmp);
788                         meta_w->writeMetadata();
789                 } catch (Exiv2::Error &) {}
790
791                 // If all went well, return new path
792                 rotate_s->insert(0, "ok");
793                 unconv_path(*rotate_s, _retval);
794                 delete rotate_s;
795                 return NS_OK;
796         }
797
798         // Otherwise yell about it
799         catch (Magick::Exception & e) {
800                 if(path_s) {
801                         delete path_s;
802                         path_s = 0;
803                 }
804                 if (rotate_s) {
805                         delete rotate_s;
806                         rotate_s = 0;
807                 }
808                 char * o = (char *)e.what();
809                 while (*o) { _retval.Append(*o++); }
810         }
811         return NS_OK;
812
813 }
814
815 // Resize an image and store it to the profile
816 NS_IMETHODIMP flGM::Resize(PRInt32 square, const nsAString & path, nsAString & _retval) {
817         string * path_s = 0;
818         string * resize_s = 0;
819         try {
820                 path_s = conv_path(path, false);
821                 if (!path_s) { return NS_ERROR_INVALID_ARG; }
822
823                 // Yank out all the metadata we want to save
824                 Exiv2::ExifData exif;
825                 Exiv2::IptcData iptc;
826                 Exiv2::XmpData xmp;
827                 try {
828                         Exiv2::Image::AutoPtr meta_r = Exiv2::ImageFactory::open(*path_s);
829                         meta_r->readMetadata();
830                         exif = meta_r->exifData();
831                         iptc = meta_r->iptcData();
832                         xmp = meta_r->xmpData();
833                 } catch (Exiv2::Error &) {}
834
835                 // Open the image
836                 Magick::Image img(*path_s);
837                 int bw = img.baseColumns(), bh = img.baseRows();
838                 int base = bw > bh ? bw : bh;
839
840                 // In the special -1 case, find the next-smallest size to scale to
841                 if (-1 == square) {
842                         if (base > 2048) {
843                                 square = 2048;
844                         } else if (base > 1600) {
845                                 square = 1600;
846                         } else if (base > 1280) {
847                                 square = 1280;
848                         } else {
849                                 square = 800;
850                         }
851                 }
852
853                 // Don't resize if we're already that size
854                 if (base <= square) {
855                         _retval = path;
856                         return NS_OK;
857                 }
858
859                 // Find resized width and height
860                 float r;
861                 ostringstream out;
862                 if (bw > bh) {
863                         r = (float)bh * (float)square / (float)bw;
864                         r = round(r);
865                         exif_update_dim(exif, square, r);
866                         out << square << "x" << r;
867                 } else {
868                         r = (float)bw * (float)square / (float)bh;
869                         r = round(r);
870                         exif_update_dim(exif, r, square);
871                         out << r << "x" << square;
872                 }
873                 string dim(out.str());
874
875                 // Create a new path
876                 resize_s = find_path(path_s, "-resize");
877                 if (!resize_s) { return NS_ERROR_NULL_POINTER; }
878                 delete path_s; path_s = 0;
879                 out << *resize_s;
880
881                 // Resize the image
882                 img.scale(dim);
883                 img.compressType(Magick::NoCompression);
884                 img.write(*resize_s);
885
886                 // Put saved metadata into the resized image
887                 try {
888                         Exiv2::Image::AutoPtr meta_w = Exiv2::ImageFactory::open(*resize_s);
889                         meta_w->setExifData(exif);
890                         meta_w->setIptcData(iptc);
891                         meta_w->setXmpData(xmp);
892                         meta_w->writeMetadata();
893                 } catch (Exiv2::Error &) {}
894                 delete resize_s; resize_s = 0;
895
896                 // If all went well, return stuff
897                 string o_s = out.str();
898                 unconv_path(o_s, _retval);
899
900                 return NS_OK;
901         }
902
903         // Otherwise yell about it
904         catch (Magick::Exception & e) {
905                 if (path_s) {
906                         delete path_s;
907                         path_s = 0;
908                 }
909                 if (resize_s) {
910                         delete resize_s;
911                         resize_s = 0;
912                 }
913                 char * o = (char *)e.what();
914                 while (*o) {
915                         _retval.Append(*o++);
916                 }
917         }
918         return NS_OK;
919
920 }
921
922 NS_IMETHODIMP flGM::Keyframe(PRInt32 square, const nsAString & path, nsAString & _retval) {
923         string * path_s = conv_path(path, false);
924         if (!path_s) { return NS_ERROR_INVALID_ARG; }
925
926         // Open the video file and decode it
927         AVFormatContext *format_ctx;
928         if (av_open_input_file(&format_ctx, path_s->c_str(), 0, 0, 0)) {
929                 return NS_ERROR_NULL_POINTER;
930         }
931         if (0 > av_find_stream_info(format_ctx)) { return NS_ERROR_NULL_POINTER; }
932         //dump_format(format_ctx, 0, path_s->c_str(), false);
933
934         int stream = -1;
935         for (int i = 0; i < format_ctx->nb_streams; ++i) {
936                 if (CODEC_TYPE_VIDEO == format_ctx->streams[i]->codec->codec_type) {
937                         stream = i;
938                         break;
939                 }
940         }
941         if (-1 == stream) { return NS_ERROR_NULL_POINTER; }
942         AVCodecContext * codec_ctx = format_ctx->streams[stream]->codec;
943
944         // Load the actual codec we found and open the video stream
945         AVCodec * codec = avcodec_find_decoder(codec_ctx->codec_id);
946         if (!codec) { return NS_ERROR_NULL_POINTER; }
947
948         // Inform the codec that we can handle truncated bitstreams -- i.e.,
949         // bitstreams where frame boundaries can fall in the middle of packets
950         if(codec->capabilities & CODEC_CAP_TRUNCATED)
951                 codec_ctx->flags|=CODEC_FLAG_TRUNCATED;
952
953         if (0 > avcodec_open(codec_ctx, codec)) { return NS_ERROR_NULL_POINTER; }
954         AVFrame * video_frame = avcodec_alloc_frame();
955         AVFrame * img_frame = avcodec_alloc_frame();
956         if (!video_frame || !img_frame) { return NS_ERROR_NULL_POINTER; }
957
958         // Report the duration
959         ostringstream out;
960         out << format_ctx->duration / AV_TIME_BASE << "###";
961
962         // HACK: Need to decode a first video frame for the consequent seek to work in case of mpeg => must do some kind of initialisation in the decoder
963         AVPacket packet;
964         int have_frame = 0;
965         int nTotalBytesDecoded = 0;
966         int nBytesDecoded = 0;
967         while (!have_frame && 0 <= av_read_frame(format_ctx, &packet)) {
968                 if (packet.stream_index == stream) { //use this packet
969                         if(format_ctx->file_size && packet.pos > .25 * format_ctx->file_size) {// don't bother searching further
970                                 av_free_packet(&packet);
971                                 break;
972                         }
973                         nTotalBytesDecoded = 0;
974                         while(!have_frame && nTotalBytesDecoded < packet.size) {
975                                 nBytesDecoded = avcodec_decode_video(codec_ctx, video_frame, &have_frame,
976                                         packet.data + nTotalBytesDecoded, packet.size);
977                                 if (nBytesDecoded < 0) {
978                                         return NS_ERROR_FILE_UNKNOWN_TYPE;
979                                 }
980                                 nTotalBytesDecoded += nBytesDecoded;
981                         }
982                 }
983                 av_free_packet(&packet);
984         }
985         if(!have_frame) {
986                 return NS_ERROR_FILE_UNKNOWN_TYPE;
987         }
988         // Play through 15% of the video
989         double dSeekInSec = (format_ctx->start_time + 0.15 * format_ctx->duration) / AV_TIME_BASE;
990         int64_t seek = dSeekInSec * (double)format_ctx->streams[stream]->time_base.den / format_ctx->streams[stream]->time_base.num;
991
992
993         struct SwsContext *toRGB_convert_ctx = NULL;
994
995         // Seek to the exact frame we want
996         if (0 < av_seek_frame(format_ctx, stream, seek, 0))
997                 return NS_ERROR_FILE_UNKNOWN_TYPE;
998         avcodec_flush_buffers(codec_ctx);
999
1000         have_frame = 0;
1001         while (0 <= av_read_frame(format_ctx, &packet)) {
1002                 if (packet.stream_index == stream) { //use this packet
1003                         nTotalBytesDecoded = 0;
1004                         while(!have_frame && nTotalBytesDecoded < packet.size) {
1005                                 nBytesDecoded = avcodec_decode_video(codec_ctx, video_frame, &have_frame,
1006                                         packet.data + nTotalBytesDecoded, packet.size);
1007                                 if (nBytesDecoded < 0) {
1008                                         return NS_ERROR_FILE_UNKNOWN_TYPE;
1009                                 }
1010                                 nTotalBytesDecoded += nBytesDecoded;
1011                         }
1012                         if (have_frame) {
1013                                 int nBytes = avpicture_get_size(PIX_FMT_RGB24, codec_ctx->width,
1014                                         codec_ctx->height);
1015                                 uint8_t * buffer = (uint8_t *)av_malloc(nBytes * sizeof(uint8_t));
1016                                 if (!buffer) { return NS_ERROR_NULL_POINTER; }
1017                                 avpicture_fill((AVPicture *)img_frame, buffer, PIX_FMT_RGB24,
1018                                         codec_ctx->width, codec_ctx->height);
1019                                 //img_convert((AVPicture *)img_frame, PIX_FMT_RGB24,
1020                                 //      (AVPicture*)video_frame, codec_ctx->pix_fmt,
1021                                 //      codec_ctx->width, codec_ctx->height);
1022
1023                                 toRGB_convert_ctx = sws_getContext(
1024                                         codec_ctx->width, codec_ctx->height, codec_ctx->pix_fmt,
1025                                         codec_ctx->width, codec_ctx->height, PIX_FMT_RGB24,
1026                                         sws_flags, NULL, NULL, NULL);
1027                                 if (toRGB_convert_ctx == NULL) {
1028                                         return NS_ERROR_NULL_POINTER;
1029                                 }
1030
1031                                 // img_convert parameters are          2 first destination, then 4 source
1032                                 // sws_scale   parameters are context, 4 first source,      then 2 destination
1033                                 sws_scale(toRGB_convert_ctx,
1034                                         video_frame->data, video_frame->linesize, 0, codec_ctx->height,
1035                                         img_frame->data, img_frame->linesize);
1036
1037                                 // Convert the keyframe to a PPM in a byte array
1038                                 char header[32];
1039                                 sprintf(header, "P6\n%d %d\n255\n", codec_ctx->width,
1040                                         codec_ctx->height);
1041                                 int size = strlen(header) + 3 * codec_ctx->width *
1042                                         codec_ctx->height;
1043                                 char * bytes = (char *)malloc(size);
1044                                 if (!bytes) { return NS_ERROR_NULL_POINTER; }
1045                                 memcpy(bytes, header, strlen(header));
1046                                 char * bytes_p = bytes + strlen(header);
1047                                 int b = codec_ctx->width * 3;
1048                                 int jj = codec_ctx->height;
1049                                 for (int j = 0; j < jj; ++j) {
1050                                         memcpy(bytes_p, img_frame->data[0] + j *
1051                                                 img_frame->linesize[0], b);
1052                                         bytes_p += b;
1053                                 }
1054
1055                                 // Convert the PPM array to a JPEG on disk
1056                                 string * thumb_s = 0;
1057                                 try {
1058                                         Magick::Image img(Magick::Blob(bytes, size));
1059
1060                                         // Output the size of the video and the embedded
1061                                         // timestamp
1062                                         int bw = img.baseColumns(), bh = img.baseRows();
1063                                         out << bw << "###" << bh << "###"
1064                                                 << format_ctx->timestamp << "###";
1065
1066                                         // Resize the output as a thumbnail
1067                                         //   This is almost certainly overkill, as I've never
1068                                         //   seen a video in portrait mode
1069                                         ostringstream dim;
1070                                         if (bw > bh) {
1071                                                 float r = (float)bh * (float)square / (float)bw;
1072                                                 out << square << "###" << round(r);
1073                                                 dim << square << "x" << round(r);
1074                                         } else {
1075                                                 float r = (float)bw * (float)square / (float)bh;
1076                                                 out << round(r) << "###" << square;
1077                                                 dim << round(r) << "x" << square;
1078                                         }
1079                                         out << "###";
1080
1081                                         // Resize and save the thumbnail
1082                                         img.scale(dim.str());
1083                                         img.compressType(Magick::NoCompression);
1084                                         path_s->append(".jpg");
1085                                         thumb_s = find_path(path_s, "-thumb");
1086                                         if (!thumb_s) { return NS_ERROR_NULL_POINTER; }
1087                                         delete path_s; path_s = 0;
1088                                         img.write(*thumb_s);
1089
1090                                         // If all went well, return stuff
1091                                         string out_s = out.str();
1092                                         char * o = (char *)out_s.c_str();
1093                                         nsCString utf8;
1094                                         while (*o) { utf8.Append(*o++); }
1095                                         _retval.Append(NS_ConvertUTF8toUTF16(utf8));
1096                                         unconv_path(*thumb_s, _retval);
1097                                         delete thumb_s; thumb_s = 0;
1098
1099                                 } catch (Magick::Exception & e) {
1100                                         if (path_s) {
1101                                                 delete path_s;
1102                                                 path_s = 0;
1103                                         }
1104                                         if (thumb_s) {
1105                                                 delete thumb_s;
1106                                                 thumb_s = 0;
1107                                         }
1108                                         char * o = (char *)e.what();
1109                                         while (*o) { _retval.Append(*o++); }
1110                                 }
1111
1112                                 free(bytes);
1113                                 av_free_packet(&packet);
1114                                 av_free(buffer);               
1115                                 break;
1116                         }
1117
1118
1119                 }
1120                 av_free_packet(&packet);
1121         }
1122
1123         // Clean yo' mess
1124         av_free(img_frame);
1125         av_free(video_frame);
1126         avcodec_close(codec_ctx);
1127         av_close_input_file(format_ctx);
1128         if(toRGB_convert_ctx) {
1129                 sws_freeContext(toRGB_convert_ctx);
1130         }
1131
1132         return NS_OK;
1133 }
1134
1135 /* void upload (in AString path); */
1136 NS_IMETHODIMP flGM::Upload(PRInt16 photoId, const nsAString & version, const nsACString & preLoad, const nsAString & path, const nsAString & destination, const nsAString & boundary) {
1137 #ifdef WIN32
1138         CFile file;
1139         if(FALSE == file.Open(nsString(path).get(), CFile::modeRead | CFile::shareDenyWrite)){
1140                 return NS_ERROR_FILE_INVALID_PATH;
1141         }
1142         ULONGLONG nFileSize = file.GetLength();
1143
1144         CInternetSession session;
1145         session.EnableStatusCallback(FALSE);
1146        
1147         // For EndHTTPRequest not to throw when uploading big files. Go figure out why!
1148         DWORD dwDataSendTimeout, dwSendTimeout, dwReceiveTimeout;
1149         dwDataSendTimeout = dwSendTimeout = dwReceiveTimeout = 150000;
1150         session.SetOption(INTERNET_OPTION_DATA_SEND_TIMEOUT, dwDataSendTimeout);
1151         session.SetOption(INTERNET_OPTION_SEND_TIMEOUT, dwSendTimeout);
1152         session.SetOption(INTERNET_OPTION_RECEIVE_TIMEOUT, dwReceiveTimeout);
1153
1154         CHttpConnection* pConnection = session.GetHttpConnection(nsString(destination).get());
1155         if(!pConnection)
1156                 return NS_ERROR_FAILURE;
1157
1158         CHttpFile* pHTTP = pConnection->OpenRequest(CHttpConnection::HTTP_VERB_POST, L"/services/upload/");
1159         if(!pHTTP)
1160                 return NS_ERROR_NULL_POINTER;
1161
1162         try {
1163                 if( 0 == pHTTP->AddRequestHeaders(GenerateHeader( nsString(version).get(), nsString(boundary).get() ))) {
1164                         DWORD dwErr= GetLastError();
1165                         return NS_ERROR_FAILURE;
1166                 }
1167
1168                 CString fileName = file.GetFileName();
1169
1170                 CString strPreFile;
1171                 strPreFile.AppendFormat(_T("--%s\r\nContent-Disposition: form-data; name=\"photo\"; filename=\"%s\"\r\nContent-Type: application/octet-stream\r\n\r\n"),
1172                         nsString(boundary).get(), fileName);
1173                 int nPreSize = preLoad.Length() + strPreFile.GetLength();
1174                 nPreSize = strlen(nsCString(preLoad).get()) + strlen(CW2A(strPreFile));
1175
1176                 CString strEndData;
1177                 strEndData.AppendFormat(_T("\r\n--%s--\r\n"), nsString(boundary).get());
1178
1179                 DWORD dwFullRequestLength = nPreSize + strEndData.GetLength() + nFileSize;
1180                 //bool bTest = (strEndData.GetLength() == strlen(CW2A(strEndData)));
1181                 // send request and beginning of the form
1182                 pHTTP->SendRequestEx(dwFullRequestLength);
1183                 pHTTP->Write(nsCString(preLoad).get(), preLoad.Length());
1184                 pHTTP->Write(CW2A(strPreFile), strPreFile.GetLength());
1185
1186                 // read the file by chunk and write it to HTTPFile
1187                 // allocate the buffer between the file and the post transaction
1188                 void* pBuffer = malloc(dwChunckSize);
1189                 if(NULL == pBuffer) {
1190                         return NS_ERROR_OUT_OF_MEMORY;
1191                 }
1192
1193                 DWORD dwReadLength = -1;
1194                 //ULONGLONG ullWritten = 0;
1195                 while(!gbCancel && (dwReadLength != 0))
1196                 {
1197                         dwReadLength = file.Read(pBuffer, dwChunckSize);
1198                         if(0!=dwReadLength)
1199                                 pHTTP->Write(pBuffer, dwReadLength);
1200                 //      ullWritten += dwReadLength;
1201                         if (m_Observer)
1202                                 m_Observer->OnProgress(nFileSize - file.GetPosition(), photoId);
1203                 }
1204                 //bool bTest2 = (ullWritten == nFileSize);
1205                 pHTTP->Write(CW2A(strEndData), strEndData.GetLength());
1206                 pHTTP->Flush();
1207                 if(NULL != pBuffer)
1208                         free(pBuffer);
1209                 if(!gbCancel) // otherwise this throws because not all the request has been sent I assume
1210                         pHTTP->EndRequest();
1211         }
1212         catch (CInternetException & exception)
1213         {
1214                 DWORD dwError = exception.m_dwError;
1215         }
1216         catch(...)
1217         {
1218         }
1219
1220         // file is uploaded
1221         // Get the response and pass it back to the app
1222         CString fullResponse = "";
1223         DWORD dwResponseLength;
1224         LPSTR szResponse;
1225         DWORD dwStatusCode;
1226         pHTTP->QueryInfoStatusCode(dwStatusCode);
1227         while (0 != (dwResponseLength = pHTTP->GetLength())) {
1228                 szResponse = (LPSTR)malloc(dwResponseLength + 1);
1229                 szResponse[dwResponseLength] = '\0';
1230                 pHTTP->Read(szResponse, dwResponseLength);
1231                 fullResponse += szResponse;
1232                 free(szResponse);
1233         }
1234         if(m_Observer) {       
1235                 m_Observer->OnResponse(nsString(fullResponse),photoId);
1236         }
1237
1238         pHTTP->Close();
1239         delete pHTTP;
1240         pHTTP = 0;
1241         file.Close();
1242         session.Close();
1243         delete pConnection;
1244         pConnection = 0;
1245         if(fullResponse.Find(_T("<ticketid>")) != -1)
1246                 return NS_OK;
1247         else if(!gbCancel)
1248                 return NS_ERROR_FAILURE;
1249         else
1250                 return NS_ERROR_ABORT;
1251 #else
1252         return NS_ERROR_UNEXPECTED;
1253 #endif
1254 }
1255
1256 /* void cancel (in boolean bCancel); */
1257 NS_IMETHODIMP flGM::Cancel(PRBool bCancel)
1258 {
1259         gbCancel = (bCancel == PR_TRUE);
1260     return NS_OK;
1261 }
1262
1263 /* void sleep (in long tick); */
1264 //NS_IMETHODIMP flGM::Sleep(PRInt32 tick)
1265 //{
1266 //      return PR_Sleep(tick);
1267 //}
Note: See TracBrowser for help on using the browser.