flickcurl_upload_status static *_flickr_api_upload_photo( dt_storage_flickr_params_t *p, char *fname, char *caption, char *description, gint imgid ) { flickcurl_upload_params *params = g_malloc(sizeof(flickcurl_upload_params)); flickcurl_upload_status *status; memset(params,0,sizeof(flickcurl_upload_params)); params->safety_level = 1; //Defaults to safe photos params->content_type = 1; //Defaults to photo (we don't support video!) params->title = caption; params->description = description; if (imgid) params->tags = dt_tag_get_list(imgid, ","); params->photo_file = fname; //fname should be the URI of temp file params->is_public = (int) p->public_perm; params->is_friend = (int) p->friend_perm; params->is_family = (int) p->family_perm; status = flickcurl_photos_upload_params(p->flickr_api->fc, params); if (!status) { fprintf (stderr,"[flickr] Something went wrong when uploading"); g_free (params); return NULL; } g_free(params); return status; }
static int _picasa_api_upload_photo( _picasa_api_context_t *ctx, char *mime , char *data, int size , char *caption, char *description, gint imgid ) { _buffer_t buffer; memset(&buffer,0,sizeof(_buffer_t)); char uri[4096]= {0}; gchar *entry = g_markup_printf_escaped ( "<entry xmlns='http://www.w3.org/2005/Atom'>" "<title>%s</title>" "<summary>%s</summary>" "<category scheme=\"http://schemas.google.com/g/2005#kind\"" " term=\"http://schemas.google.com/photos/2007#photo\"/>" "</entry>", caption,description); // Hack for nonform multipart post... gchar mpart1[4096]= {0}; gchar *mpart_format="Media multipart posting\n--END_OF_PART\nContent-Type: application/atom+xml\n\n%s\n--END_OF_PART\nContent-Type: %s\n\n"; sprintf(mpart1,mpart_format,entry,mime); int mpart1size=strlen(mpart1); int postdata_length=mpart1size+size+strlen("\n--END_OF_PART--"); gchar *postdata=g_malloc(postdata_length); memcpy( postdata, mpart1, mpart1size); memcpy( postdata+mpart1size, data, size); memcpy( postdata+mpart1size+size, "\n--END_OF_PART--",strlen("\n--END_OF_PART--") ); struct curl_slist *headers = NULL; headers = curl_slist_append(headers,ctx->authHeader); headers = curl_slist_append(headers,"Content-Type: multipart/related; boundary=\"END_OF_PART\""); headers = curl_slist_append(headers,"MIME-version: 1.0"); headers = curl_slist_append(headers,"Expect:"); headers = curl_slist_append(headers,"GData-Version: 2"); sprintf(uri,"http://picasaweb.google.com/data/feed/api/user/default/albumid/%s", ctx->current_album->id); curl_easy_setopt(ctx->curl_handle, CURLOPT_URL, uri); #ifdef _DEBUG curl_easy_setopt(ctx->curl_handle, CURLOPT_VERBOSE, 1); #else curl_easy_setopt(ctx->curl_handle, CURLOPT_VERBOSE, 0); #endif curl_easy_setopt(ctx->curl_handle, CURLOPT_HTTPHEADER, headers); curl_easy_setopt(ctx->curl_handle, CURLOPT_UPLOAD,0); // A post request ! curl_easy_setopt(ctx->curl_handle, CURLOPT_POST,1); curl_easy_setopt(ctx->curl_handle, CURLOPT_POSTFIELDS, postdata); curl_easy_setopt(ctx->curl_handle, CURLOPT_POSTFIELDSIZE, postdata_length); curl_easy_setopt(ctx->curl_handle, CURLOPT_WRITEFUNCTION, _picasa_api_buffer_write_func); curl_easy_setopt(ctx->curl_handle, CURLOPT_WRITEDATA, &buffer); curl_easy_perform( ctx->curl_handle ); curl_slist_free_all(headers); long result; curl_easy_getinfo(ctx->curl_handle,CURLINFO_RESPONSE_CODE,&result ); // If we want to add tags let's do... if( result == 201 && imgid > 0 ) { // Image was created , fine.. and result have the fully created photo xml entry.. // Let's perform an update of the photos keywords with tags passed along to this function.. // and use picasa photo update api to add keywords to the photo... // Build the keywords content string gchar *keywords = NULL; keywords = dt_tag_get_list(imgid, ","); xmlDocPtr doc; xmlNodePtr entryNode; // Parse xml document if( ( doc = xmlParseDoc( (xmlChar *)buffer.data ))==NULL) return 0; entryNode = xmlDocGetRootElement(doc); if( !xmlStrcmp(entryNode->name, (const xmlChar *) "entry") ) { // Let's get the gd:etag attribute of entry... // For now, we force update using "If-Match: *" /* if( !xmlHasProp(entryNode, (const xmlChar*)"gd:etag") ) return 0; xmlChar *etag = xmlGetProp(entryNode,(const xmlChar*)"gd:etag"); */ gchar *photo_id=NULL; gchar *updateUri=NULL; xmlNodePtr entryChilds = entryNode->xmlChildrenNode; if( entryChilds != NULL ) { do { if ((!xmlStrcmp(entryChilds->name, (const xmlChar *)"id")) ) { // Get the photo id used in uri for update xmlChar *id= xmlNodeListGetString(doc, entryChilds->xmlChildrenNode, 1); if( xmlStrncmp( id, (const xmlChar *)"http://",7) ) photo_id = g_strdup((const char *)id); xmlFree(id); } else if ((!xmlStrcmp(entryChilds->name, (const xmlChar *)"group")) ) { // Got the media:group entry lets find the child media:keywords xmlNodePtr mediaChilds = entryChilds->xmlChildrenNode; if(mediaChilds != NULL) { do { // Got the keywords tag, let's set the tags if ((!xmlStrcmp(mediaChilds->name, (const xmlChar *)"keywords")) ) xmlNodeSetContent(mediaChilds, (xmlChar *)keywords); } while( (mediaChilds = mediaChilds->next)!=NULL ); } } else if (( !xmlStrcmp(entryChilds->name,(const xmlChar*)"link")) ) { xmlChar *rel = xmlGetProp(entryChilds,(const xmlChar*)"rel"); if( !xmlStrcmp(rel,(const xmlChar *)"edit") ) { updateUri=(char *)xmlGetProp(entryChilds,(const xmlChar*)"href"); } xmlFree(rel); } } while( (entryChilds = entryChilds->next)!=NULL ); } // Let's update the photo struct curl_slist *headers = NULL; headers = curl_slist_append(headers,ctx->authHeader); headers = curl_slist_append(headers,"Content-Type: application/atom+xml"); headers = curl_slist_append(headers,"If-Match: *"); headers = curl_slist_append(headers,"Expect:"); headers = curl_slist_append(headers,"GData-Version: 2"); _buffer_t response; memset(&response,0,sizeof(_buffer_t)); // Setup data to send.. _buffer_t writebuffer; int datasize; xmlDocDumpMemory(doc,(xmlChar **)&writebuffer.data, &datasize); writebuffer.size = datasize; writebuffer.offset=0; curl_easy_setopt(ctx->curl_handle, CURLOPT_URL, updateUri); #ifdef _DEBUG curl_easy_setopt(ctx->curl_handle, CURLOPT_VERBOSE, 1); #else curl_easy_setopt(ctx->curl_handle, CURLOPT_VERBOSE, 0); #endif curl_easy_setopt(ctx->curl_handle, CURLOPT_HTTPHEADER, headers); curl_easy_setopt(ctx->curl_handle, CURLOPT_UPLOAD,1); // A put request curl_easy_setopt(ctx->curl_handle, CURLOPT_READDATA,&writebuffer); curl_easy_setopt(ctx->curl_handle, CURLOPT_INFILESIZE,writebuffer.size); curl_easy_setopt(ctx->curl_handle, CURLOPT_READFUNCTION,_picasa_api_buffer_read_func); curl_easy_setopt(ctx->curl_handle, CURLOPT_WRITEFUNCTION, _picasa_api_buffer_write_func); curl_easy_setopt(ctx->curl_handle, CURLOPT_WRITEDATA, &response); curl_easy_perform( ctx->curl_handle ); xmlFree( updateUri ); xmlFree( writebuffer.data ); if (response.data != NULL) g_free(response.data); if (photo_id != NULL) g_free(photo_id); // FIXME: never used! curl_slist_free_all( headers ); } } return result; }
int store(dt_imageio_module_storage_t *self, dt_imageio_module_data_t *sdata, const int imgid, dt_imageio_module_format_t *format, dt_imageio_module_data_t *fdata, const int num, const int total, const gboolean high_quality, const gboolean upscale, dt_colorspaces_color_profile_type_t icc_type, const gchar *icc_filename, dt_iop_color_intent_t icc_intent) { dt_storage_piwigo_gui_data_t *ui = self->gui_data; gint result = 0; const char *ext = format->extension(fdata); // Let's upload image... /* construct a temporary file name */ char fname[PATH_MAX] = { 0 }; dt_loc_get_tmp_dir(fname, sizeof(fname)); g_strlcat(fname, "/darktable.XXXXXX.", sizeof(fname)); g_strlcat(fname, ext, sizeof(fname)); char *caption = NULL; char *description = NULL; char *author = NULL; gint fd = g_mkstemp(fname); if(fd == -1) { dt_control_log("failed to create temporary image for piwigo export"); fprintf(stderr, "failed to create tempfile: %s\n", fname); return 1; } close(fd); const dt_image_t *img = dt_image_cache_get(darktable.image_cache, imgid, 'r'); // If title is not existing, then use the filename without extension. If not, then use title instead GList *title = dt_metadata_get(img->id, "Xmp.dc.title", NULL); if(title != NULL) { caption = g_strdup(title->data); g_list_free_full(title, &g_free); } else { caption = g_path_get_basename(img->filename); (g_strrstr(caption, "."))[0] = '\0'; // chop extension... } GList *desc = dt_metadata_get(img->id, "Xmp.dc.description", NULL); if(desc != NULL) { description = g_strdup(desc->data); g_list_free_full(desc, &g_free); } dt_image_cache_read_release(darktable.image_cache, img); GList *auth = dt_metadata_get(img->id, "Xmp.dc.creator", NULL); if(auth != NULL) { author = g_strdup(auth->data); g_list_free_full(auth, &g_free); } if(dt_imageio_export(imgid, fname, format, fdata, high_quality, upscale, FALSE, icc_type, icc_filename, icc_intent, self, sdata, num, total) != 0) { fprintf(stderr, "[imageio_storage_piwigo] could not export to file: `%s'!\n", fname); dt_control_log(_("could not export to file `%s'!"), fname); result = 1; goto cleanup; } dt_pthread_mutex_lock(&darktable.plugin_threadsafe); { gboolean status = TRUE; dt_storage_piwigo_params_t *p = (dt_storage_piwigo_params_t *)sdata; if(p->export_tags) { GList *tags_list = dt_tag_get_list(imgid); if(p->tags) g_free(p->tags); p->tags = dt_util_glist_to_str(",", tags_list); g_list_free_full(tags_list, g_free); } if(p->new_album) { status = _piwigo_api_create_new_album(p); if(!status) dt_control_log(_("cannot create a new piwigo album!")); } if(status) { status = _piwigo_api_upload_photo(p, fname, author, caption, description); if(!status) { fprintf(stderr, "[imageio_storage_piwigo] could not upload to piwigo!\n"); dt_control_log(_("could not upload to piwigo!")); result = 1; } else if (p->new_album) { // we do not want to create more albums when multiple upload p->new_album = FALSE; _piwigo_refresh_albums(ui, p->album); } } } dt_pthread_mutex_unlock(&darktable.plugin_threadsafe); cleanup: // And remove from filesystem.. g_unlink(fname); g_free(caption); g_free(description); g_free(author); if(!result) { // this makes sense only if the export was successful dt_control_log(ngettext("%d/%d exported to piwigo webalbum", "%d/%d exported to piwigo webalbum", num), num, total); } return result; }