tests: fix up unit tests for playbin2/decodebin2 renames and updates
[platform/upstream/gstreamer.git] / ext / gnomevfs / gstgnomevfssink.c
1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *                    2000 Wim Taymans <wtay@chello.be>
4  *                    2001 Bastien Nocera <hadess@hadess.net>
5  *                    2003 Colin Walters <walters@verbum.org>
6  *                    2005 Tim-Philipp Müller <tim centricular net>
7  *
8  * gstgnomevfssink.c: 
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Library General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Library General Public License for more details.
19  *
20  * You should have received a copy of the GNU Library General Public
21  * License along with this library; if not, write to the
22  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23  * Boston, MA 02111-1307, USA.
24  */
25
26 /**
27  * SECTION:element-gnomevfssink
28  * @see_also: #GstFileSink, #GstGnomeVFSSrc
29  *
30  * This plugin writes incoming data to a local or remote location specified
31  * by an URI. This location can be specified using any protocol supported by
32  * the GnomeVFS library. Common protocols are 'file', 'ftp', or 'smb'.
33  *
34  * Applications can connect to the #GstGnomeVFSSink::allow-overwrite signal to
35  * receive a callback when an existing file will be overwritten. The return
36  * value of the signal will determine if gnomevfssink will overwrite the
37  * resource or abort with an error.
38  *
39  * <refsect2>
40  * <title>Example launch lines</title>
41  * |[
42  * gst-launch -v filesrc location=input.xyz ! gnomevfssink location=file:///home/joe/out.xyz
43  * ]| The above pipeline will simply copy a local file. Instead of gnomevfssink,
44  * we could just as well have used the filesink element here.
45  * |[
46  * gst-launch -v filesrc location=foo.mp3 ! mad ! flacenc ! gnomevfssink location=smb://othercomputer/foo.flac
47  * ]| The above pipeline will re-encode an mp3 file into FLAC format and store
48  * it on a remote host using the Samba protocol.
49  * </refsect2>
50  *
51  * Last reviewed on 2006-02-28 (0.10.4)
52  */
53
54 #ifdef HAVE_CONFIG_H
55 #include "config.h"
56 #endif
57
58 #include "gstgnomevfssink.h"
59
60 #include "gst/gst-i18n-plugin.h"
61
62 #include <gst/gst.h>
63 #include <libgnomevfs/gnome-vfs.h>
64 #include <string.h>
65 #include <errno.h>
66
67 enum
68 {
69   SIGNAL_ERASE_ASK,
70   LAST_SIGNAL
71 };
72
73 enum
74 {
75   ARG_0,
76   ARG_LOCATION,
77   ARG_URI,
78   ARG_HANDLE
79 };
80
81 static void gst_gnome_vfs_sink_finalize (GObject * obj);
82
83 static void gst_gnome_vfs_sink_uri_handler_init (gpointer g_iface,
84     gpointer iface_data);
85
86 static void gst_gnome_vfs_sink_set_property (GObject * object, guint prop_id,
87     const GValue * value, GParamSpec * pspec);
88 static void gst_gnome_vfs_sink_get_property (GObject * object, guint prop_id,
89     GValue * value, GParamSpec * pspec);
90
91 static gboolean gst_gnome_vfs_sink_open_file (GstGnomeVFSSink * sink);
92 static void gst_gnome_vfs_sink_close_file (GstGnomeVFSSink * sink);
93 static gboolean gst_gnome_vfs_sink_start (GstBaseSink * basesink);
94 static gboolean gst_gnome_vfs_sink_stop (GstBaseSink * basesink);
95 static GstFlowReturn gst_gnome_vfs_sink_render (GstBaseSink * basesink,
96     GstBuffer * buffer);
97 static gboolean gst_gnome_vfs_sink_handle_event (GstBaseSink * basesink,
98     GstEvent * event);
99 static gboolean gst_gnome_vfs_sink_query (GstPad * pad, GstQuery * query);
100
101 static guint gst_gnome_vfs_sink_signals[LAST_SIGNAL];   /* all 0 */
102
103 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
104     GST_PAD_SINK,
105     GST_PAD_ALWAYS,
106     GST_STATIC_CAPS_ANY);
107
108 GST_DEBUG_CATEGORY_STATIC (gst_gnome_vfs_sink_debug);
109 #define GST_CAT_DEFAULT gst_gnome_vfs_sink_debug
110
111 static void
112 gst_gnome_vfs_sink_do_init (GType type)
113 {
114   static const GInterfaceInfo urihandler_info = {
115     gst_gnome_vfs_sink_uri_handler_init,
116     NULL,
117     NULL
118   };
119
120   g_type_add_interface_static (type, GST_TYPE_URI_HANDLER, &urihandler_info);
121
122   GST_DEBUG_CATEGORY_INIT (gst_gnome_vfs_sink_debug, "gnomevfssink", 0,
123       "Gnome VFS sink element");
124 }
125
126 #define gst_gnome_vfs_sink_parent_class parent_class
127 G_DEFINE_TYPE_WITH_CODE (GstGnomeVFSSink, gst_gnome_vfs_sink,
128     GST_TYPE_BASE_SINK, gst_gnome_vfs_sink_do_init (g_define_type_id));
129
130 static gboolean
131 _gst_boolean_allow_overwrite_accumulator (GSignalInvocationHint * ihint,
132     GValue * return_accu, const GValue * handler_return, gpointer dummy)
133 {
134   gboolean allow_overwrite;
135
136   allow_overwrite = g_value_get_boolean (handler_return);
137   if (!(ihint->run_type & G_SIGNAL_RUN_CLEANUP))
138     g_value_set_boolean (return_accu, allow_overwrite);
139
140   /* stop emission if signal doesn't allow overwriting */
141   return allow_overwrite;
142 }
143
144 static void
145 gst_gnome_vfs_sink_class_init (GstGnomeVFSSinkClass * klass)
146 {
147   GstBaseSinkClass *basesink_class;
148   GstElementClass *gstelement_class;
149   GObjectClass *gobject_class;
150
151   gobject_class = (GObjectClass *) klass;
152   gstelement_class = (GstElementClass *) klass;
153   basesink_class = (GstBaseSinkClass *) klass;
154
155   gobject_class->set_property = gst_gnome_vfs_sink_set_property;
156   gobject_class->get_property = gst_gnome_vfs_sink_get_property;
157   gobject_class->finalize = gst_gnome_vfs_sink_finalize;
158
159   g_object_class_install_property (gobject_class, ARG_LOCATION,
160       g_param_spec_string ("location", "File Location",
161           "Location of the file to write", NULL,
162           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
163   g_object_class_install_property (gobject_class, ARG_URI,
164       g_param_spec_boxed ("uri", "GnomeVFSURI", "URI for GnomeVFS",
165           GST_TYPE_GNOME_VFS_URI, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
166   g_object_class_install_property (gobject_class, ARG_HANDLE,
167       g_param_spec_boxed ("handle", "GnomeVFSHandle", "Handle for GnomeVFS",
168           GST_TYPE_GNOME_VFS_HANDLE,
169           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
170
171   /**
172    * GstGnomeVFSSink::allow-overwrite
173    * @sink: the object which received the signal
174    * @uri: the URI to be overwritten
175    *
176    * This signal is fired when gnomevfssink is about to overwrite an
177    * existing resource. The application can connect to this signal and ask
178    * the user if the resource may be overwritten. 
179    *
180    * Returns: A boolean indicating that the resource may be overwritten.
181    */
182   gst_gnome_vfs_sink_signals[SIGNAL_ERASE_ASK] =
183       g_signal_new ("allow-overwrite", G_TYPE_FROM_CLASS (klass),
184       G_SIGNAL_RUN_CLEANUP, G_STRUCT_OFFSET (GstGnomeVFSSinkClass, erase_ask),
185       _gst_boolean_allow_overwrite_accumulator, NULL,
186       gst_marshal_BOOLEAN__POINTER, G_TYPE_BOOLEAN, 1, GST_TYPE_GNOME_VFS_URI);
187
188   gst_element_class_add_pad_template (gstelement_class,
189       gst_static_pad_template_get (&sinktemplate));
190
191   gst_element_class_set_details_simple (gstelement_class,
192       "GnomeVFS Sink", "Sink/File",
193       "Write a stream to a GnomeVFS URI", "Bastien Nocera <hadess@hadess.net>");
194
195   basesink_class->stop = GST_DEBUG_FUNCPTR (gst_gnome_vfs_sink_stop);
196   basesink_class->start = GST_DEBUG_FUNCPTR (gst_gnome_vfs_sink_start);
197   basesink_class->event = GST_DEBUG_FUNCPTR (gst_gnome_vfs_sink_handle_event);
198   basesink_class->render = GST_DEBUG_FUNCPTR (gst_gnome_vfs_sink_render);
199   basesink_class->get_times = NULL;
200 }
201
202 static void
203 gst_gnome_vfs_sink_finalize (GObject * obj)
204 {
205   GstGnomeVFSSink *sink = GST_GNOME_VFS_SINK (obj);
206
207   if (sink->uri) {
208     gnome_vfs_uri_unref (sink->uri);
209     sink->uri = NULL;
210   }
211
212   if (sink->uri_name) {
213     g_free (sink->uri_name);
214     sink->uri_name = NULL;
215   }
216
217   G_OBJECT_CLASS (parent_class)->finalize (obj);
218 }
219
220 static void
221 gst_gnome_vfs_sink_init (GstGnomeVFSSink * sink)
222 {
223   gst_pad_set_query_function (GST_BASE_SINK_PAD (sink),
224       GST_DEBUG_FUNCPTR (gst_gnome_vfs_sink_query));
225
226   sink->uri = NULL;
227   sink->uri_name = NULL;
228   sink->handle = NULL;
229   sink->own_handle = FALSE;
230   sink->current_pos = 0;
231
232   GST_BASE_SINK (sink)->sync = FALSE;
233 }
234
235 static void
236 gst_gnome_vfs_sink_set_property (GObject * object, guint prop_id,
237     const GValue * value, GParamSpec * pspec)
238 {
239   GstGnomeVFSSink *sink;
240   GstState cur_state;
241
242   sink = GST_GNOME_VFS_SINK (object);
243
244   gst_element_get_state (GST_ELEMENT (sink), &cur_state, NULL, 0);
245
246   if (cur_state == GST_STATE_PLAYING || cur_state == GST_STATE_PAUSED) {
247     GST_WARNING_OBJECT (sink, "cannot set property when PAUSED or PLAYING");
248     return;
249   }
250
251   GST_OBJECT_LOCK (sink);
252
253   switch (prop_id) {
254     case ARG_LOCATION:{
255       const gchar *new_location;
256
257       if (sink->uri) {
258         gnome_vfs_uri_unref (sink->uri);
259         sink->uri = NULL;
260       }
261       if (sink->uri_name) {
262         g_free (sink->uri_name);
263         sink->uri_name = NULL;
264       }
265
266       new_location = g_value_get_string (value);
267       if (new_location) {
268         sink->uri_name = gst_gnome_vfs_location_to_uri_string (new_location);
269         sink->uri = gnome_vfs_uri_new (sink->uri_name);
270       }
271       break;
272     }
273     case ARG_URI:{
274       if (sink->uri) {
275         gnome_vfs_uri_unref (sink->uri);
276         sink->uri = NULL;
277       }
278       if (sink->uri_name) {
279         g_free (sink->uri_name);
280         sink->uri_name = NULL;
281       }
282       if (g_value_get_boxed (value)) {
283         sink->uri = (GnomeVFSURI *) g_value_dup_boxed (value);
284         sink->uri_name = gnome_vfs_uri_to_string (sink->uri, 0);
285       }
286       break;
287     }
288     case ARG_HANDLE:{
289       if (sink->uri) {
290         gnome_vfs_uri_unref (sink->uri);
291         sink->uri = NULL;
292       }
293       if (sink->uri_name) {
294         g_free (sink->uri_name);
295         sink->uri_name = NULL;
296       }
297       sink->handle = g_value_get_boxed (value);
298       break;
299     }
300     default:
301       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
302       break;
303   }
304
305   GST_OBJECT_UNLOCK (sink);
306 }
307
308 static void
309 gst_gnome_vfs_sink_get_property (GObject * object, guint prop_id,
310     GValue * value, GParamSpec * pspec)
311 {
312   GstGnomeVFSSink *sink;
313
314   sink = GST_GNOME_VFS_SINK (object);
315
316   GST_OBJECT_LOCK (sink);
317
318   switch (prop_id) {
319     case ARG_LOCATION:
320       g_value_set_string (value, sink->uri_name);
321       break;
322     case ARG_URI:
323       g_value_set_boxed (value, sink->uri);
324       break;
325     case ARG_HANDLE:
326       g_value_set_boxed (value, sink->handle);
327       break;
328     default:
329       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
330       break;
331   }
332
333   GST_OBJECT_UNLOCK (sink);
334 }
335
336 static gboolean
337 gst_gnome_vfs_sink_open_file (GstGnomeVFSSink * sink)
338 {
339   GnomeVFSResult result;
340
341   if (sink->uri) {
342     /* open the file, all permissions, umask will apply */
343     result = gnome_vfs_create_uri (&(sink->handle), sink->uri,
344         GNOME_VFS_OPEN_WRITE, TRUE,
345         GNOME_VFS_PERM_USER_READ | GNOME_VFS_PERM_USER_WRITE |
346         GNOME_VFS_PERM_GROUP_READ | GNOME_VFS_PERM_GROUP_WRITE |
347         GNOME_VFS_PERM_OTHER_READ | GNOME_VFS_PERM_OTHER_WRITE);
348
349     /* if the file existed and the property says to ask, then ask! */
350     if (result == GNOME_VFS_ERROR_FILE_EXISTS) {
351       gboolean erase_anyway = FALSE;
352
353       g_signal_emit (G_OBJECT (sink),
354           gst_gnome_vfs_sink_signals[SIGNAL_ERASE_ASK], 0, sink->uri,
355           &erase_anyway);
356       if (erase_anyway) {
357         result = gnome_vfs_create_uri (&(sink->handle), sink->uri,
358             GNOME_VFS_OPEN_WRITE, FALSE,
359             GNOME_VFS_PERM_USER_READ | GNOME_VFS_PERM_USER_WRITE |
360             GNOME_VFS_PERM_GROUP_READ | GNOME_VFS_PERM_GROUP_WRITE |
361             GNOME_VFS_PERM_OTHER_READ | GNOME_VFS_PERM_OTHER_WRITE);
362       }
363     }
364
365     GST_DEBUG_OBJECT (sink, "open: %s", gnome_vfs_result_to_string (result));
366
367     if (result != GNOME_VFS_OK) {
368       gchar *filename = gnome_vfs_uri_to_string (sink->uri,
369           GNOME_VFS_URI_HIDE_PASSWORD);
370
371       GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE,
372           (_("Could not open vfs file \"%s\" for writing: %s."),
373               filename, gnome_vfs_result_to_string (result)), GST_ERROR_SYSTEM);
374       g_free (filename);
375       return FALSE;
376     }
377     sink->own_handle = TRUE;
378   } else if (!sink->handle) {
379     GST_ELEMENT_ERROR (sink, RESOURCE, FAILED, (_("No filename given")),
380         (NULL));
381     return FALSE;
382   } else {
383     sink->own_handle = FALSE;
384   }
385
386   sink->current_pos = 0;
387
388   return TRUE;
389 }
390
391 static void
392 gst_gnome_vfs_sink_close_file (GstGnomeVFSSink * sink)
393 {
394   GnomeVFSResult result;
395
396   if (sink->own_handle) {
397     /* close the file */
398     result = gnome_vfs_close (sink->handle);
399
400     if (result != GNOME_VFS_OK) {
401       gchar *filename = gnome_vfs_uri_to_string (sink->uri,
402           GNOME_VFS_URI_HIDE_PASSWORD);
403
404       GST_ELEMENT_ERROR (sink, RESOURCE, CLOSE,
405           (_("Could not close vfs file \"%s\"."), filename), GST_ERROR_SYSTEM);
406       g_free (filename);
407     }
408
409     sink->own_handle = FALSE;
410     sink->handle = NULL;
411   }
412 }
413
414 static gboolean
415 gst_gnome_vfs_sink_start (GstBaseSink * basesink)
416 {
417   gboolean ret;
418
419   ret = gst_gnome_vfs_sink_open_file (GST_GNOME_VFS_SINK (basesink));
420
421   return ret;
422 }
423
424 static gboolean
425 gst_gnome_vfs_sink_stop (GstBaseSink * basesink)
426 {
427   GST_DEBUG_OBJECT (basesink, "closing ...");
428   gst_gnome_vfs_sink_close_file (GST_GNOME_VFS_SINK (basesink));
429   return TRUE;
430 }
431
432 static gboolean
433 gst_gnome_vfs_sink_handle_event (GstBaseSink * basesink, GstEvent * event)
434 {
435   GstGnomeVFSSink *sink;
436   gboolean ret = TRUE;
437
438   sink = GST_GNOME_VFS_SINK (basesink);
439
440   GST_DEBUG_OBJECT (sink, "processing %s event", GST_EVENT_TYPE_NAME (event));
441
442   switch (GST_EVENT_TYPE (event)) {
443     case GST_EVENT_SEGMENT:{
444       GnomeVFSResult res;
445       const GstSegment *segment;
446
447       gst_event_parse_segment (event, &segment);
448
449       if (segment->format != GST_FORMAT_BYTES) {
450         GST_WARNING_OBJECT (sink, "ignored NEWSEGMENT event in %s format",
451             gst_format_get_name (segment->format));
452         break;
453       }
454
455       GST_LOG_OBJECT (sink, "seeking to offset %" G_GINT64_FORMAT,
456           segment->start);
457       res = gnome_vfs_seek (sink->handle, GNOME_VFS_SEEK_START, segment->start);
458
459       if (res != GNOME_VFS_OK) {
460         GST_ERROR_OBJECT (sink, "Failed to seek to offset %"
461             G_GINT64_FORMAT ": %s", segment->start,
462             gnome_vfs_result_to_string (res));
463         ret = FALSE;
464       } else {
465         sink->current_pos = segment->start;
466       }
467
468       break;
469     }
470
471     case GST_EVENT_FLUSH_START:
472     case GST_EVENT_EOS:{
473       /* No need to flush with GnomeVfs */
474       break;
475     }
476     default:
477       break;
478   }
479
480   return ret;
481 }
482
483 static gboolean
484 gst_gnome_vfs_sink_query (GstPad * pad, GstQuery * query)
485 {
486   GstGnomeVFSSink *sink;
487   GstFormat format;
488
489   sink = GST_GNOME_VFS_SINK (GST_PAD_PARENT (pad));
490
491   switch (GST_QUERY_TYPE (query)) {
492     case GST_QUERY_POSITION:
493       gst_query_parse_position (query, &format, NULL);
494       switch (format) {
495         case GST_FORMAT_DEFAULT:
496         case GST_FORMAT_BYTES:
497           gst_query_set_position (query, GST_FORMAT_BYTES, sink->current_pos);
498           return TRUE;
499         default:
500           return FALSE;
501       }
502
503     case GST_QUERY_FORMATS:
504       gst_query_set_formats (query, 2, GST_FORMAT_DEFAULT, GST_FORMAT_BYTES);
505       return TRUE;
506
507     case GST_QUERY_URI:
508       gst_query_set_uri (query, sink->uri_name);
509       return TRUE;
510
511     default:
512       return gst_pad_query_default (pad, query);
513   }
514 }
515
516 static GstFlowReturn
517 gst_gnome_vfs_sink_render (GstBaseSink * basesink, GstBuffer * buf)
518 {
519   GnomeVFSFileSize written, cur_pos;
520   GstGnomeVFSSink *sink;
521   GnomeVFSResult result;
522   GstFlowReturn ret;
523   guint8 *data;
524   gsize size;
525
526   sink = GST_GNOME_VFS_SINK (basesink);
527
528   if (gnome_vfs_tell (sink->handle, &cur_pos) == GNOME_VFS_OK) {
529     /* bring up to date with current position for proper reporting */
530     sink->current_pos = cur_pos;
531   }
532
533   data = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
534   result = gnome_vfs_write (sink->handle, data, size, &written);
535   gst_buffer_unmap (buf, data, size);
536
537   switch (result) {
538     case GNOME_VFS_OK:{
539       GST_DEBUG_OBJECT (sink, "wrote %" G_GINT64_FORMAT " bytes at %"
540           G_GINT64_FORMAT, (gint64) written, (gint64) cur_pos);
541
542       if (written < size) {
543         /* FIXME: what to do here? (tpm) */
544         g_warning ("%s: %" G_GSIZE_FORMAT " bytes should be written, only %"
545             G_GUINT64_FORMAT " bytes written", G_STRLOC, size, written);
546       }
547
548       sink->current_pos += size;
549       ret = GST_FLOW_OK;
550       break;
551     }
552     case GNOME_VFS_ERROR_NO_SPACE:{
553       /* TODO: emit signal/send msg on out-of-diskspace and
554        * handle this gracefully (see open bug) (tpm) */
555       GST_ELEMENT_ERROR (sink, RESOURCE, NO_SPACE_LEFT, (NULL),
556           ("bufsize=%u, written=%u", size, (guint) written));
557       ret = GST_FLOW_ERROR;
558       break;
559     }
560     default:{
561       gchar *filename = gnome_vfs_uri_to_string (sink->uri,
562           GNOME_VFS_URI_HIDE_PASSWORD);
563
564       GST_ELEMENT_ERROR (sink, RESOURCE, WRITE,
565           (_("Error while writing to file \"%s\"."), filename),
566           ("%s, bufsize=%u, written=%u", gnome_vfs_result_to_string (result),
567               size, (guint) written));
568
569       g_free (filename);
570       ret = GST_FLOW_ERROR;
571       break;
572     }
573   }
574
575   return ret;
576 }
577
578 /*** GSTURIHANDLER INTERFACE *************************************************/
579
580 static GstURIType
581 gst_gnome_vfs_sink_uri_get_type (void)
582 {
583   return GST_URI_SINK;
584 }
585
586 static gchar **
587 gst_gnome_vfs_sink_uri_get_protocols (void)
588 {
589   return gst_gnomevfs_get_supported_uris ();
590 }
591
592 static const gchar *
593 gst_gnome_vfs_sink_uri_get_uri (GstURIHandler * handler)
594 {
595   GstGnomeVFSSink *sink = GST_GNOME_VFS_SINK (handler);
596
597   return sink->uri_name;
598 }
599
600 static gboolean
601 gst_gnome_vfs_sink_uri_set_uri (GstURIHandler * handler, const gchar * uri)
602 {
603   GstGnomeVFSSink *sink = GST_GNOME_VFS_SINK (handler);
604   GstState cur_state;
605
606   gst_element_get_state (GST_ELEMENT (sink), &cur_state, NULL, 0);
607
608   if (cur_state == GST_STATE_PLAYING || cur_state == GST_STATE_PAUSED) {
609     GST_WARNING_OBJECT (sink, "cannot set uri when PAUSED or PLAYING");
610     return FALSE;
611   }
612
613   g_object_set (sink, "location", uri, NULL);
614
615   return TRUE;
616 }
617
618 static void
619 gst_gnome_vfs_sink_uri_handler_init (gpointer g_iface, gpointer iface_data)
620 {
621   GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
622
623   iface->get_type = gst_gnome_vfs_sink_uri_get_type;
624   iface->get_protocols = gst_gnome_vfs_sink_uri_get_protocols;
625   iface->get_uri = gst_gnome_vfs_sink_uri_get_uri;
626   iface->set_uri = gst_gnome_vfs_sink_uri_set_uri;
627 }