static size_t gst_curl_smtp_sink_transfer_data_buffer (GstCurlBaseSink * bcsink, void *curl_ptr, size_t block_size, guint * last_chunk) { GstCurlSmtpSink *sink = GST_CURL_SMTP_SINK (bcsink); size_t bytes_to_send; if (sink->payload_headers && sink->payload_headers->len) { return transfer_payload_headers (sink, curl_ptr, block_size); } if (sink->base64_chunk != NULL) { bytes_to_send = transfer_chunk (curl_ptr, bcsink->transfer_buf, sink->base64_chunk, block_size, last_chunk); GST_OBJECT_LOCK (sink); if (sink->eos) { gst_curl_smtp_sink_notify_transfer_end_unlocked (sink); } GST_OBJECT_UNLOCK (sink); return bytes_to_send; } /* we should never get here */ return 0; }
static gboolean gst_curl_smtp_sink_set_payload_headers_unlocked (GstCurlBaseSink * bcsink) { GstCurlSmtpSink *sink = GST_CURL_SMTP_SINK (bcsink); gchar *hdrs; gboolean append_headers = FALSE; if (sink->reset_transfer_options) { g_assert (!bcsink->is_live); sink->reset_transfer_options = FALSE; /* all data has been sent in the previous transfer, setup headers for * a new transfer */ gst_curl_smtp_sink_set_transfer_options_unlocked (bcsink); append_headers = TRUE; } if (sink->payload_headers == NULL) { sink->payload_headers = g_byte_array_new (); append_headers = TRUE; } if (sink->base64_chunk == NULL) { g_assert (!bcsink->is_live); /* we are just about to send the very first attachment in this transfer. * This is the only place where base64_chunk and its array are allocated. */ sink->base64_chunk = g_malloc (sizeof (Base64Chunk)); sink->base64_chunk->chunk_array = g_byte_array_new (); append_headers = TRUE; } else { g_assert (sink->base64_chunk->chunk_array != NULL); g_assert (sink->base64_chunk->chunk_array->len == 0); } sink->base64_chunk->state = 0; sink->base64_chunk->save = 0; if (G_UNLIKELY (!append_headers)) { if (sink->base64_chunk != NULL) { g_byte_array_free (sink->base64_chunk->chunk_array, TRUE); sink->base64_chunk->chunk_array = NULL; g_free (sink->base64_chunk); sink->base64_chunk = NULL; } return FALSE; } hdrs = g_strdup_printf ("\r\n\r\n--%s\r\n" "Content-Type: application/octet-stream; name=\"%s\"\r\n" /* TODO: support for other encodings */ "Content-Transfer-Encoding: BASE64\r\n" "Content-Disposition: attachment; filename=\"%s\"\r\n\r\n" "\r\n", BOUNDARY_STRING, bcsink->file_name, bcsink->file_name); g_byte_array_append (sink->payload_headers, (guint8 *) hdrs, strlen (hdrs)); g_free (hdrs); return TRUE; }
static size_t gst_curl_smtp_sink_flush_data_unlocked (GstCurlBaseSink * bcsink, void *curl_ptr, size_t block_size, gboolean new_file) { GstCurlSmtpSink *sink = GST_CURL_SMTP_SINK (bcsink); Base64Chunk *chunk = sink->base64_chunk; gint state = chunk->state; gint save = chunk->save; GByteArray *array = chunk->chunk_array; size_t bytes_to_send; gint len; gchar *data_out; if ((bcsink->is_live && (sink->nbr_attachments_left == sink->nbr_attachments)) || (sink->nbr_attachments == 1) || sink->eos) { bcsink->is_live = FALSE; sink->reset_transfer_options = TRUE; GST_DEBUG ("returning 0, no more data to send in this transfer"); return 0; } /* it will need up to 5 bytes if line-breaking is enabled, however an * additional byte is needed for <CR> as it is not automatically added by glib */ data_out = g_malloc (6); len = g_base64_encode_close (TRUE, data_out, &state, &save); chunk->state = state; chunk->save = save; /* workaround */ data_out[len - 1] = '\r'; data_out[len] = '\n'; /* +1 for CR */ g_byte_array_append (array, (guint8 *) data_out, (guint) (len + 1)); g_free (data_out); if (new_file) { sink->nbr_attachments_left--; bcsink->is_live = TRUE; if (sink->nbr_attachments_left <= 1) { sink->nbr_attachments_left = sink->nbr_attachments; } /* reset flag */ bcsink->new_file = FALSE; /* set payload headers for new file */ gst_curl_smtp_sink_set_payload_headers_unlocked (bcsink); } bytes_to_send = MIN (block_size, array->len); memcpy ((guint8 *) curl_ptr, array->data, bytes_to_send); g_byte_array_remove_range (array, 0, bytes_to_send); return bytes_to_send; }
static gboolean gst_curl_smtp_sink_prepare_transfer (GstCurlBaseSink * bcsink) { GstCurlSmtpSink *sink = GST_CURL_SMTP_SINK (bcsink); CURLcode res; gboolean ret = TRUE; if (sink->pop_location && strlen (sink->pop_location)) { if ((sink->pop_curl = curl_easy_init ()) == NULL) { bcsink->error = g_strdup ("POP protocol: failed to create handler"); return FALSE; } res = curl_easy_setopt (sink->pop_curl, CURLOPT_URL, sink->pop_location); if (res != CURLE_OK) { bcsink->error = g_strdup_printf ("failed to set URL: %s", curl_easy_strerror (res)); return FALSE; } if (sink->pop_user != NULL && strlen (sink->pop_user) && sink->pop_passwd != NULL && strlen (sink->pop_passwd)) { res = curl_easy_setopt (sink->pop_curl, CURLOPT_USERNAME, sink->pop_user); if (res != CURLE_OK) { bcsink->error = g_strdup_printf ("failed to set user name: %s", curl_easy_strerror (res)); return FALSE; } res = curl_easy_setopt (sink->pop_curl, CURLOPT_PASSWORD, sink->pop_passwd); if (res != CURLE_OK) { bcsink->error = g_strdup_printf ("failed to set user name: %s", curl_easy_strerror (res)); return FALSE; } } } if (sink->pop_curl != NULL) { /* ready to initialize connection to POP server */ res = curl_easy_perform (sink->pop_curl); if (res != CURLE_OK) { bcsink->error = g_strdup_printf ("POP transfer failed: %s", curl_easy_strerror (res)); ret = FALSE; } curl_easy_cleanup (sink->pop_curl); sink->pop_curl = NULL; } return ret; }
static void // FIXME: exactly the same function as in http sink gst_curl_smtp_sink_set_mime_type (GstCurlBaseSink * bcsink, GstCaps * caps) { GstCurlSmtpSink *sink = GST_CURL_SMTP_SINK (bcsink); GstStructure *structure; const gchar *mime_type; if (sink->content_type != NULL) { return; } structure = gst_caps_get_structure (caps, 0); mime_type = gst_structure_get_name (structure); sink->content_type = g_strdup (mime_type); }
static void gst_curl_smtp_sink_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { GstCurlSmtpSink *sink; g_return_if_fail (GST_IS_CURL_SMTP_SINK (object)); sink = GST_CURL_SMTP_SINK (object); switch (prop_id) { case PROP_MAIL_RCPT: g_value_set_string (value, sink->mail_rcpt); break; case PROP_MAIL_FROM: g_value_set_string (value, sink->mail_from); break; case PROP_SUBJECT: g_value_set_string (value, sink->subject); break; case PROP_MESSAGE_BODY: g_value_set_string (value, sink->message_body); break; case PROP_CONTENT_TYPE: g_value_set_string (value, sink->content_type); break; case PROP_USE_SSL: g_value_set_boolean (value, sink->use_ssl); break; case PROP_NBR_ATTACHMENTS: g_value_set_int (value, sink->nbr_attachments); break; case PROP_POP_USER_NAME: g_value_set_string (value, sink->pop_user); break; case PROP_POP_USER_PASSWD: g_value_set_string (value, sink->pop_passwd); break; case PROP_POP_LOCATION: g_value_set_string (value, sink->pop_location); break; default: GST_DEBUG_OBJECT (sink, "invalid property id"); break; } }
static gboolean gst_curl_smtp_sink_prepare_transfer (GstCurlBaseSink * bcsink) { GstCurlSmtpSink *sink = GST_CURL_SMTP_SINK (bcsink); CURLcode res; gboolean ret = TRUE; if (sink->pop_location && strlen (sink->pop_location)) { if ((sink->pop_curl = curl_easy_init ()) == NULL) { GST_DEBUG_OBJECT (sink, "POP protocol: failed to create handler"); GST_ELEMENT_ERROR (sink, RESOURCE, WRITE, ("POP protocol: failed to create handler"), (NULL)); return FALSE; } curl_easy_setopt (sink->pop_curl, CURLOPT_URL, sink->pop_location); if (sink->pop_user != NULL && strlen (sink->pop_user) && sink->pop_passwd != NULL && strlen (sink->pop_passwd)) { curl_easy_setopt (sink->pop_curl, CURLOPT_USERNAME, sink->pop_user); curl_easy_setopt (sink->pop_curl, CURLOPT_PASSWORD, sink->pop_passwd); } } if (sink->pop_curl == NULL) { goto end; } /* ready to initialize connection to POP server */ res = curl_easy_perform (sink->pop_curl); if (res != CURLE_OK) { GST_DEBUG_OBJECT (sink, "POP transfer failed: %s", curl_easy_strerror (res)); GST_ELEMENT_ERROR (sink, RESOURCE, WRITE, ("POP transfer failed: %s", curl_easy_strerror (res)), (NULL)); ret = FALSE; } curl_easy_cleanup (sink->pop_curl); sink->pop_curl = NULL; end: return ret; }
static gboolean gst_curl_smtp_sink_has_buffered_data_unlocked (GstCurlBaseSink * bcsink) { GstCurlSmtpSink *sink = GST_CURL_SMTP_SINK (bcsink); Base64Chunk *chunk; GByteArray *array = NULL; gboolean ret = FALSE; chunk = sink->base64_chunk; if (chunk) { array = chunk->chunk_array; if (array) ret = (array->len == 0 && sink->final_boundary_added) ? FALSE : TRUE; } return ret; }
static void gst_curl_smtp_sink_finalize (GObject * gobject) { GstCurlSmtpSink *this = GST_CURL_SMTP_SINK (gobject); GST_DEBUG ("finalizing curlsmtpsink"); if (this->curl_recipients != NULL) { curl_slist_free_all (this->curl_recipients); } g_free (this->mail_rcpt); g_free (this->mail_from); g_free (this->subject); g_free (this->message_body); g_free (this->content_type); g_cond_clear (&this->cond_transfer_end); if (this->base64_chunk != NULL) { if (this->base64_chunk->chunk_array != NULL) { g_byte_array_free (this->base64_chunk->chunk_array, TRUE); } g_free (this->base64_chunk); } if (this->payload_headers != NULL) { g_byte_array_free (this->payload_headers, TRUE); } g_free (this->pop_user); g_free (this->pop_passwd); if (this->pop_curl != NULL) { curl_easy_cleanup (this->pop_curl); this->pop_curl = NULL; } g_free (this->pop_location); G_OBJECT_CLASS (parent_class)->finalize (gobject); }
static size_t gst_curl_smtp_sink_transfer_data_buffer (GstCurlBaseSink * bcsink, void *curl_ptr, size_t block_size, guint * last_chunk) { GstCurlSmtpSink *sink = GST_CURL_SMTP_SINK (bcsink); size_t bytes_to_send; if (sink->payload_headers && sink->payload_headers->len) { return transfer_payload_headers (sink, curl_ptr, block_size); } if (sink->base64_chunk != NULL) { bytes_to_send = transfer_chunk (curl_ptr, bcsink->transfer_buf, sink->base64_chunk, block_size, last_chunk); /* if last chunk of current buffer and max attachments per mail is reached * then add final boundary */ if (*last_chunk && sink->curr_attachment == sink->nbr_attachments && !sink->final_boundary_added) { add_final_boundary_unlocked (sink); /* now that we've added the final boundary to the array we have on more * chunk to send */ *last_chunk = 0; } GST_OBJECT_LOCK (sink); if (sink->eos) { gst_curl_smtp_sink_notify_transfer_end_unlocked (sink); } GST_OBJECT_UNLOCK (sink); return bytes_to_send; } /* we should never get here */ return 0; }
static gboolean gst_curl_smtp_sink_event (GstBaseSink * bsink, GstEvent * event) { GstCurlBaseSink *bcsink = GST_CURL_BASE_SINK (bsink); GstCurlSmtpSink *sink = GST_CURL_SMTP_SINK (bsink); switch (event->type) { case GST_EVENT_EOS: GST_DEBUG_OBJECT (sink, "received EOS"); gst_curl_base_sink_set_live (bcsink, FALSE); GST_OBJECT_LOCK (sink); sink->eos = TRUE; GST_OBJECT_UNLOCK (sink); if (sink->base64_chunk != NULL) add_final_boundary_unlocked (sink); gst_curl_base_sink_transfer_thread_notify_unlocked (bcsink); GST_OBJECT_LOCK (sink); if (sink->base64_chunk != NULL && bcsink->flow_ret == GST_FLOW_OK) { gst_curl_smtp_sink_wait_for_transfer_end_unlocked (sink); } GST_OBJECT_UNLOCK (sink); gst_curl_base_sink_transfer_thread_close (bcsink); break; default: break; } return GST_BASE_SINK_CLASS (parent_class)->event (bsink, event); }
/* Setup header fields (From:/To:/Date: etc) and message body for the e-mail. * This data is supposed to be sent to libcurl just before any media data. * This function is called once for each e-mail: * 1. we are about the send the first attachment * 2. we have sent all the attachments and continue sending new ones within * a new e-mail (transfer options have been reset). */ static gboolean gst_curl_smtp_sink_set_transfer_options_unlocked (GstCurlBaseSink * bcsink) { GstCurlSmtpSink *sink = GST_CURL_SMTP_SINK (bcsink); GstCurlTlsSinkClass *parent_class; gchar *request_headers; GDateTime *date; gchar *date_str; gchar **tmp_list = NULL; gchar *subject_header = NULL; gchar *message_body = NULL; gchar *rcpt_header = NULL; gchar *enc_rcpt; gchar *from_header = NULL; gchar *enc_from; gint i; g_assert (sink->payload_headers == NULL); g_assert (sink->mail_rcpt != NULL); g_assert (sink->mail_from != NULL); /* time */ date = g_date_time_new_now_local (); date_str = g_date_time_format (date, "%a %b %e %H:%M:%S %Y"); g_date_time_unref (date); /* recipient, sender and subject are all UTF-8 strings, which are additionally * base64-encoded */ /* recipient */ enc_rcpt = generate_encoded_word (sink->mail_rcpt); rcpt_header = g_strdup_printf ("%s <%s>", enc_rcpt, sink->mail_rcpt); g_free (enc_rcpt); /* sender */ enc_from = generate_encoded_word (sink->mail_from); from_header = g_strdup_printf ("%s <%s>", enc_from, sink->mail_from); g_free (enc_from); /* subject */ if (sink->subject != NULL) { subject_header = generate_encoded_word (sink->subject); } /* message */ if (sink->message_body != NULL) { message_body = g_base64_encode ((const guchar *) sink->message_body, strlen (sink->message_body)); } request_headers = g_strdup_printf ( /* headers */ "To: %s\r\n" "From: %s\r\n" "Subject: %s\r\n" "Date: %s\r\n" MIME_VERSION "\r\n" "Content-Type: multipart/mixed; boundary=%s\r\n" "\r\n" /* body headers */ "--" BOUNDARY_STRING "\r\n" "Content-Type: text/plain; charset=utf-8\r\n" "Content-Transfer-Encoding: BASE64\r\n" /* message body */ "\r\n%s\r\n", rcpt_header, from_header, subject_header ? subject_header : "", date_str, BOUNDARY_STRING, message_body ? message_body : ""); sink->payload_headers = g_byte_array_new (); g_byte_array_append (sink->payload_headers, (guint8 *) request_headers, strlen (request_headers)); g_free (date_str); g_free (subject_header); g_free (message_body); g_free (rcpt_header); g_free (from_header); g_free (request_headers); curl_easy_setopt (bcsink->curl, CURLOPT_MAIL_FROM, sink->mail_from); if (sink->curl_recipients != NULL) { curl_slist_free_all (sink->curl_recipients); sink->curl_recipients = NULL; } tmp_list = g_strsplit_set (sink->mail_rcpt, MAIL_RCPT_DELIMITER, -1); for (i = 0; i < g_strv_length (tmp_list); i++) { sink->curl_recipients = curl_slist_append (sink->curl_recipients, tmp_list[i]); } g_strfreev (tmp_list); /* note that the CURLOPT_MAIL_RCPT takes a list, not a char array */ curl_easy_setopt (bcsink->curl, CURLOPT_MAIL_RCPT, sink->curl_recipients); parent_class = GST_CURL_TLS_SINK_GET_CLASS (sink); if (sink->use_ssl) { return parent_class->set_options_unlocked (bcsink); } return TRUE; }
static void gst_curl_smtp_sink_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GstCurlSmtpSink *sink; GstState cur_state; g_return_if_fail (GST_IS_CURL_SMTP_SINK (object)); sink = GST_CURL_SMTP_SINK (object); gst_element_get_state (GST_ELEMENT (sink), &cur_state, NULL, 0); if (cur_state != GST_STATE_PLAYING && cur_state != GST_STATE_PAUSED) { GST_OBJECT_LOCK (sink); switch (prop_id) { case PROP_MAIL_RCPT: g_free (sink->mail_rcpt); sink->mail_rcpt = g_value_dup_string (value); GST_DEBUG_OBJECT (sink, "mail-rcpt set to %s", sink->mail_rcpt); break; case PROP_MAIL_FROM: g_free (sink->mail_from); sink->mail_from = g_value_dup_string (value); GST_DEBUG_OBJECT (sink, "mail-from set to %s", sink->mail_from); break; case PROP_SUBJECT: g_free (sink->subject); sink->subject = g_value_dup_string (value); GST_DEBUG_OBJECT (sink, "subject set to %s", sink->subject); break; case PROP_MESSAGE_BODY: g_free (sink->message_body); sink->message_body = g_value_dup_string (value); GST_DEBUG_OBJECT (sink, "message-body set to %s", sink->message_body); break; case PROP_CONTENT_TYPE: g_free (sink->content_type); sink->content_type = g_value_dup_string (value); GST_DEBUG_OBJECT (sink, "content-type set to %s", sink->content_type); break; case PROP_USE_SSL: sink->use_ssl = g_value_get_boolean (value); GST_DEBUG_OBJECT (sink, "use-ssl set to %d", sink->use_ssl); break; case PROP_NBR_ATTACHMENTS: sink->nbr_attachments = g_value_get_int (value); sink->nbr_attachments_left = sink->nbr_attachments; GST_DEBUG_OBJECT (sink, "nbr-attachments set to %d", sink->nbr_attachments); break; case PROP_POP_USER_NAME: g_free (sink->pop_user); sink->pop_user = g_value_dup_string (value); GST_DEBUG_OBJECT (sink, "pop-user set to %s", sink->pop_user); break; case PROP_POP_USER_PASSWD: g_free (sink->pop_passwd); sink->pop_passwd = g_value_dup_string (value); GST_DEBUG_OBJECT (sink, "pop-passwd set to %s", sink->pop_passwd); break; case PROP_POP_LOCATION: g_free (sink->pop_location); sink->pop_location = g_value_dup_string (value); GST_DEBUG_OBJECT (sink, "pop-location set to %s", sink->pop_location); break; default: GST_DEBUG_OBJECT (sink, "invalid property id %d", prop_id); break; } GST_OBJECT_UNLOCK (sink); return; } /* in PLAYING or PAUSED state */ GST_OBJECT_LOCK (sink); switch (prop_id) { case PROP_CONTENT_TYPE: g_free (sink->content_type); sink->content_type = g_value_dup_string (value); GST_DEBUG_OBJECT (sink, "content type set to %s", sink->content_type); break; default: GST_WARNING_OBJECT (sink, "cannot set property when PLAYING"); break; } GST_OBJECT_UNLOCK (sink); }
static gboolean gst_curl_smtp_sink_event (GstBaseSink * bsink, GstEvent * event) { GstCurlBaseSink *bcsink = GST_CURL_BASE_SINK (bsink); GstCurlSmtpSink *sink = GST_CURL_SMTP_SINK (bsink); GByteArray *array; gchar *boundary_end; switch (event->type) { case GST_EVENT_EOS: GST_DEBUG_OBJECT (sink, "received EOS"); gst_curl_base_sink_set_live (bcsink, FALSE); GST_OBJECT_LOCK (sink); sink->eos = TRUE; GST_OBJECT_UNLOCK (sink); if (sink->base64_chunk != NULL) { gsize len; gint save, state; gchar *data_out; array = sink->base64_chunk->chunk_array; g_assert (array); GST_DEBUG ("adding final boundary"); /* it will need up to 5 bytes if line-breaking is enabled * additional byte is needed for <CR> as it is not automatically added by glib */ data_out = g_malloc (6); save = sink->base64_chunk->save; state = sink->base64_chunk->state; len = g_base64_encode_close (TRUE, data_out, &state, &save); /* workaround */ data_out[len - 1] = '\r'; data_out[len] = '\n'; /* +1 for CR */ g_byte_array_append (array, (guint8 *) data_out, (guint) (len + 1)); g_free (data_out); boundary_end = g_strdup_printf ("\r\n%s\r\n", BOUNDARY_STRING_END); g_byte_array_append (array, (guint8 *) boundary_end, strlen (boundary_end)); g_free (boundary_end); } gst_curl_base_sink_transfer_thread_notify_unlocked (bcsink); GST_OBJECT_LOCK (sink); if (sink->base64_chunk != NULL && bcsink->flow_ret == GST_FLOW_OK) { gst_curl_smtp_sink_wait_for_transfer_end_unlocked (sink); } GST_OBJECT_UNLOCK (sink); gst_curl_base_sink_transfer_thread_close (bcsink); break; default: break; } return GST_BASE_SINK_CLASS (parent_class)->event (bsink, event); }