static gboolean __is_guid_valid(MidgardObject *self) { if(MGD_OBJECT_GUID (self) == NULL) { midgard_set_error(MGD_OBJECT_CNC (self), MGD_GENERIC_ERROR, MGD_ERR_INVALID_PROPERTY_VALUE, " Guid property is NULL for %s. ", G_OBJECT_TYPE_NAME(self)); g_warning("%s", MGD_OBJECT_CNC (self)->err->message); g_clear_error(&MGD_OBJECT_CNC (self)->err); return FALSE; } if(!midgard_is_guid(MGD_OBJECT_GUID (self))) { midgard_set_error(MGD_OBJECT_CNC (self), MGD_GENERIC_ERROR, MGD_ERR_INVALID_PROPERTY_VALUE, " Guid-property of %s has invalid value.", G_OBJECT_TYPE_NAME(self)); g_warning("%s", MGD_OBJECT_CNC (self)->err->message); g_clear_error(&MGD_OBJECT_CNC (self)->err); return FALSE; } return TRUE; }
/** * midgard_transaction_begin: * @self: #MidgardTransaction instance * * Begins new, underlying database provider's transaction. * In case of error, #MidgardConnection error is set to MGD_ERR_INTERNAL. * * Returns: %TRUE on success, %FALSE otherwise. * * Since: 9.09 */ gboolean midgard_transaction_begin (MidgardTransaction *self) { _ASSERT_T_MGD(self); gboolean rv = FALSE; GdaConnection *cnc = _T_CNC(self); MidgardConnection *mgd = self->priv->mgd; GError *error = NULL; g_debug("Begin named transaction '%s'", self->priv->name); rv = gda_connection_begin_transaction(cnc, self->priv->name, GDA_TRANSACTION_ISOLATION_UNKNOWN, &error); if (!error && rv) return TRUE; midgard_set_error(mgd, MGD_GENERIC_ERROR, MGD_ERR_INTERNAL, error && error->message ? error->message : " Unknown error."); if (error) g_error_free(error); return FALSE; }
/** * midgard_blob_write_content: * @self: #MidgardBlob self instance. * @content: content which should be written to file. * * Write given @content to a file. * * Returns: %TRUE if content has been written to file, %FALSE otherwise. */ gboolean midgard_blob_write_content(MidgardBlob *self, const gchar *content) { g_assert(self != NULL); g_assert(content != NULL); MidgardConnection *mgd = self->priv->mgd; MIDGARD_ERRNO_SET(mgd, MGD_ERR_OK); __get_filepath(self); if(!self->priv->filepath) { midgard_set_error(self->priv->mgd, MGD_GENERIC_ERROR, MGD_ERR_USER_DATA, "Invalid attachment. " "Can not read file from empty location"); return FALSE; } __get_channel(self, "w"); if(!self->priv->channel) return FALSE; GIOChannel *channel = self->priv->channel; GIOStatus status; GError *err = NULL; status = g_io_channel_write_chars(channel, content, strlen(content), NULL, &err); g_io_channel_flush(channel, NULL); if(status != G_IO_STATUS_NORMAL) { midgard_set_error(self->priv->mgd, MGD_GENERIC_ERROR, MGD_ERR_INTERNAL, " %s ", err->message); g_clear_error(&err); return FALSE; } return TRUE; }
/* Create private channel */ static void __get_channel(MidgardBlob *self, const gchar *mode) { if(self->priv->channel) return; GError *err = NULL; GIOChannel *channel = self->priv->channel; gchar *filepath = self->priv->filepath; if(channel == NULL) channel = g_io_channel_new_file(filepath, mode ? mode : "w", &err); /* Keep "binary" mode. It's useless on *nix but needed for WIN32 */ if(!channel){ midgard_set_error(self->priv->mgd, MGD_GENERIC_ERROR, MGD_ERR_USER_DATA, " %s ", err->message); g_clear_error(&err); return; } g_clear_error(&err); GIOStatus status = g_io_channel_set_encoding(channel, (const gchar *)self->priv->encoding, &err); if(status != G_IO_STATUS_NORMAL) { midgard_set_error(self->priv->mgd, MGD_GENERIC_ERROR, MGD_ERR_USER_DATA, " %s ", err->message); g_clear_error(&err); g_io_channel_shutdown(channel, TRUE, NULL); return; } self->priv->channel = channel; }
/** * midgard_connection_open: * @self: #MidgardConnection instance * @name: configuration file name * @error: pointer to store error * * Opens a connection to the database, which is defined in named configuration. * The configuration file is read from the system configuration directory * and is used as the configuration for the created connection. For example: `/etc` * directory is taken into account if library is compiled with `/usr' prefix, * `/usr/local/etc` if compiled with `/usr/local` prefix, etc. * * Consider using midgard_connection_open_config(), if you need to open connection to * database which is configured in user's home directory. * * If the named database configuration can not be read or the connection fails, * then %FALSE is returned and an error message is written to the global midgard * error state. * * It also initializes #MidgardSchema object (which is encapsulated by implementation ) * and register all MgdSchema, #MidgardObjectClass derived classes defined by user. * This happens only when basic Midgard classes are not registered in GType system. * This is recommended way to initialize MgdSchema types. * * Returns: %TRUE if the operation succeeded, %FALSE otherwise. */ gboolean midgard_connection_open (MidgardConnection *self, const char *name, GError **error) { g_assert(self != NULL); g_assert (name != NULL); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); MIDGARD_ERRNO_SET (self, MGD_ERR_OK); gboolean rv = TRUE; __SELF_REOPEN (self, rv); if (!rv) return rv; /* FIXME, it should be handled by GError */ if(self->priv->config != NULL){ midgard_set_error(self, MGD_GENERIC_ERROR, MGD_ERR_USER_DATA, "MidgardConfig already associated with " "MidgardConnection"); return FALSE; } MidgardConfig *config = midgard_config_new(); GError *rf_error = NULL; if(!midgard_config_read_file(config, name, FALSE, &rf_error)) { if(rf_error) g_propagate_error(error, rf_error); MIDGARD_ERRNO_SET (self, MGD_ERR_NOT_CONNECTED); return FALSE; } if(error) g_clear_error(&rf_error); self->priv->config = config; GHashTable *hash = NULL; if(!__midgard_connection_open(self, &hash, TRUE)) { MIDGARD_ERRNO_SET (self, MGD_ERR_NOT_CONNECTED); rv = FALSE; } g_hash_table_destroy(hash); return rv; }
/* Build attachment's full path. * Create new relative location for attachment if it's empty. * WARNING! Keep in mind that attachment's location might be changed * between midgard_blob method calls! */ static void __get_filepath(MidgardBlob *self) { if(self->priv->filepath) return; gchar *fname = NULL; gchar *location = NULL; gchar *up_a, *up_b; MidgardObject *attachment = self->priv->attachment; GParamSpec *pspec = g_object_class_find_property( G_OBJECT_GET_CLASS(G_OBJECT(attachment)), "location"); /* FIXME, we can not depend on just location property here. * This must be defined in mgdschema file. */ if(!pspec){ midgard_set_error(self->priv->mgd, MGD_GENERIC_ERROR, MGD_ERR_USER_DATA, "Invalid Object. " "Blobs can not be attached to %s", G_OBJECT_TYPE_NAME(G_OBJECT(attachment))); return; } g_object_get(G_OBJECT(self->priv->attachment), "location", &location, NULL); if(location == NULL || *location == '\0') { fname = midgard_uuid_new(); up_a = g_strdup(" "); up_a[0] = g_ascii_toupper(fname[0]); up_b = g_strdup(" "); up_b[0] = g_ascii_toupper(fname[1]); /* g_strdup is used to avoid filling every byte, which should be done with g_new(gchar, 2) */ location = g_build_path(G_DIR_SEPARATOR_S, up_a, up_b, fname, NULL); g_free(fname); g_free(up_a); g_free(up_b); g_object_set(G_OBJECT(self->priv->attachment), "location", location , NULL); } self->priv->location = location; self->priv->filepath = g_build_path(G_DIR_SEPARATOR_S, self->priv->blobdir, self->priv->location, NULL); }
/** * midgard_connection_open_from_file: * @mgd: #MidgardConnection instance * @filepath: configuration file path * @error: pointer to store error * * Opens a connection to the database. * The configuration file is read from given filepath. * * Take a look at midgard_connection_open() wrt #MidgardSchema. * * Returns: %TRUE if the operation succeeded, %FALSE otherwise. */ gboolean midgard_connection_open_from_file( MidgardConnection *mgd, const char *filepath, GError **error) { g_assert(mgd != NULL); g_assert (filepath != NULL); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); gboolean rv = TRUE; __SELF_REOPEN (mgd, rv); if (!rv) return rv; /* FIXME, it should be handled by GError */ if(mgd->priv->config != NULL){ midgard_set_error(mgd, MGD_GENERIC_ERROR, MGD_ERR_USER_DATA, "MidgardConfig already associated with " "MidgardConnection"); return FALSE; } MidgardConfig *config = midgard_config_new(); GError *rf_error = NULL; if(!midgard_config_read_file_at_path(config, filepath, &rf_error)) { if(rf_error) g_propagate_error(error, rf_error); return FALSE; } if(error) g_clear_error(&rf_error); mgd->priv->config = config; GHashTable *hash = NULL; if(!__midgard_connection_open(mgd, &hash, TRUE)) rv = FALSE; if(hash) g_hash_table_destroy(hash); return rv; }
/** * midgard_blob_new: * @attachment: #MidgardObject of MIDGARD_TYPE_ATTACHMENT type. * @encoding: file encoding * * Default encoding is UTF-8. Set NULL @encoding if such is required. * * Instatiate new Midgard Blob object for the given midgard_attachment object. * This is almost the same constructor as g_object_new, but unlike that one, * midgard_blob_new requires MidgardObject (midgard_attachment) object's pointer. * * This constructor defines new relative path for attachment, if midgard_attachment * is associated with midgard_blob and its location is empty. * In any other case, location is not changed. * * Returns: newly instatiated #MidgardBlob object or %NULL on failure */ MidgardBlob *midgard_blob_new (MidgardObject *attachment, const gchar *encoding) { g_assert(attachment != NULL); MidgardConnection *mgd = MGD_OBJECT_CNC (attachment); if(mgd == NULL) { g_critical("MidgardConnection not found for given attachment"); return NULL; } MIDGARD_ERRNO_SET(mgd, MGD_ERR_OK); const gchar *blobdir = MIDGARD_DBOBJECT (attachment)->dbpriv->mgd->priv->config->blobdir; if(!g_file_test(blobdir,G_FILE_TEST_EXISTS)) { midgard_set_error(mgd, MGD_GENERIC_ERROR, MGD_ERR_INTERNAL, " Blobs directory doesn't exist. %s", blobdir); return NULL; } if(!g_file_test(blobdir,G_FILE_TEST_IS_DIR)) { g_warning("Defined blobs directory is not directory"); return NULL; } MidgardBlob *self = g_object_new(MIDGARD_TYPE_BLOB, NULL); self->priv->attachment = attachment; self->priv->mgd = mgd; self->priv->blobdir = g_strdup(blobdir); self->priv->channel = NULL; self->priv->encoding = NULL; if(encoding != NULL) self->priv->encoding = g_strdup(encoding); __get_filepath(self); if(self->priv->filepath == NULL) { g_object_unref(self); return NULL; } return self; }
/** * midgard_blob_exists: * @self: #MidgardBlob instance * * Check if file associated with midgard_blob exists. * This function will also return FALSE, if file is not yet associated. * * Returns: %TRUE if file exists, %FALSE otherwise */ gboolean midgard_blob_exists(MidgardBlob *self) { g_assert(self != NULL); MidgardConnection *mgd = self->priv->mgd; MIDGARD_ERRNO_SET(mgd, MGD_ERR_OK); __get_filepath(self); if(!self->priv->filepath) return FALSE; if(g_file_test(self->priv->filepath, G_FILE_TEST_IS_DIR)){ midgard_set_error(self->priv->mgd, MGD_GENERIC_ERROR, MGD_ERR_INTERNAL, " '%s' is a directory ", self->priv->filepath); return FALSE; } return g_file_test(self->priv->filepath, G_FILE_TEST_IS_REGULAR); }
/** * midgard_blob_read_content: * @self: MidgardBlob self instance * @bytes_read: number of bytes read * * Returned content should be freed when no longer needed. * @bytes_read holds size of returned content. * * This function should be used to get content of small files. * For large and huge ones midgard_blob_get_handler should be used * to get file handle. * * Returns: content of the file, or %NULL on failure */ gchar *midgard_blob_read_content(MidgardBlob *self, gsize *bytes_read) { g_assert(self != NULL); MidgardConnection *mgd = self->priv->mgd; MIDGARD_ERRNO_SET(mgd, MGD_ERR_OK); __get_filepath(self); if(!self->priv->filepath) { midgard_set_error(mgd, MGD_GENERIC_ERROR, MGD_ERR_USER_DATA, "Invalid attachment. " "Can not read file from empty location"); return NULL; } __get_channel(self, "r"); if(!self->priv->channel) return NULL; GIOChannel *channel = self->priv->channel; gchar *content = NULL; GError *err = NULL; GIOStatus status; const gchar *err_msg = ""; /* Rewind. Channel could be already used for writing. */ status = g_io_channel_seek_position(channel, 0, G_SEEK_SET, &err); if(status != G_IO_STATUS_NORMAL) { if(err != NULL) err_msg = err->message; midgard_set_error(self->priv->mgd, MGD_GENERIC_ERROR, MGD_ERR_INTERNAL, " %s ", err_msg); return NULL; } g_clear_error(&err); g_io_channel_set_encoding (channel, NULL, NULL); status = g_io_channel_read_to_end(channel, &content, bytes_read, &err); /* FIXME, I have no idea how to determine file encoding */ /* Let's set UTF-8 and try again */ if(status == G_IO_STATUS_ERROR) { if(err && err->domain == G_CONVERT_ERROR){ g_io_channel_set_encoding (channel, "UTF-8", NULL); g_clear_error(&err); status = g_io_channel_read_to_end(channel, &content, bytes_read, &err); } } if(err) g_clear_error(&err); err_msg = ""; if(status == G_IO_STATUS_NORMAL && *bytes_read == 0){ if(err != NULL) err_msg = err->message; midgard_set_error(self->priv->mgd, MGD_GENERIC_ERROR, MGD_ERR_INTERNAL, " %s ", err_msg); return NULL; } if(err) g_clear_error(&err); return content; }
gboolean _nodes2object(GObject *object, xmlNode *node, gboolean force) { g_assert(object); g_assert(node); xmlNode *cur = NULL; GObject *prop_object; gchar *nodeprop = NULL; xmlChar *decoded; xmlParserCtxtPtr parser; MidgardObject *mobject = NULL; MidgardObject *lobject = NULL; MidgardReflectionProperty *mrp = NULL; const gchar *linktype = NULL; if(MIDGARD_IS_OBJECT(object)) { mobject = MIDGARD_OBJECT(object); MidgardObjectClass *klass = MIDGARD_OBJECT_GET_CLASS(mobject); if(klass) mrp = midgard_reflection_property_new( MIDGARD_DBOBJECT_CLASS(klass)); } gpointer set_from_xml_func = MIDGARD_DBOBJECT_GET_CLASS(object)->dbpriv->set_from_xml_node; if(set_from_xml_func != NULL) { MIDGARD_DBOBJECT_GET_CLASS(object)->dbpriv->set_from_xml_node(MIDGARD_DBOBJECT(object), node); return TRUE; } for (cur = node; cur; cur = cur->next) { if (cur->type == XML_ELEMENT_NODE) { linktype = NULL; GParamSpec *pspec = g_object_class_find_property( G_OBJECT_GET_CLASS(G_OBJECT(object)), (const gchar *)cur->name); if(pspec) { GValue pval = {0, }; g_value_init(&pval, pspec->value_type); if(nodeprop) g_free(nodeprop); nodeprop = (gchar *)xmlNodeGetContent(cur); if(mrp) { if(midgard_reflection_property_is_link( mrp, pspec->name)){ linktype = midgard_reflection_property_get_link_name( mrp, pspec->name); } } /* moved out from mrp condition check to avoid nested indents */ if(linktype && midgard_is_guid( (const gchar *) nodeprop)){ /* Just set property quickly, if property holds a guid */ GType mtype = midgard_reflection_property_get_midgard_type(mrp, pspec->name); if (mtype == MGD_TYPE_GUID) { g_value_unset(&pval); g_object_set(mobject, (const gchar *)cur->name, nodeprop, NULL); continue; } /* we can use nodeprop directly */ lobject = midgard_schema_object_factory_get_object_by_guid ( MIDGARD_DBOBJECT (mobject)->dbpriv->mgd, (const gchar *) nodeprop); if(!lobject && !force){ g_object_unref(mrp); g_value_unset(&pval); midgard_set_error(MGD_OBJECT_CNC (mobject), MGD_GENERIC_ERROR, MGD_ERR_MISSED_DEPENDENCE, " Can not import %s. " "No '%s' object identified by '%s'", G_OBJECT_TYPE_NAME(object), linktype, nodeprop); g_clear_error(&MIDGARD_DBOBJECT (mobject)->dbpriv->mgd->err); return FALSE; } /* When force parameter is set we do not translate guids to ids */ if(force && !lobject && midgard_is_guid( (const gchar *) nodeprop)) { switch(pspec->value_type) { case G_TYPE_UINT: g_value_set_uint(&pval, 0); break; case G_TYPE_INT: g_value_set_int(&pval, 0); break; default: goto set_property_unchecked; break; } g_object_set_property( G_OBJECT(object), (const gchar *) cur->name, &pval); g_value_unset(&pval); continue; } GValue tval = {0, }; g_value_init(&tval, pspec->value_type); g_object_get_property(G_OBJECT(lobject), "id", &tval); if(G_VALUE_TYPE(&pval) == G_TYPE_INT) g_value_transform((const GValue *) &tval, &pval); else g_value_copy((const GValue*) &tval, &pval); g_object_set_property( G_OBJECT(object), (const gchar *) cur->name, &pval); g_value_unset(&pval); g_object_unref(lobject); g_value_unset(&tval); continue; } set_property_unchecked: switch (G_TYPE_FUNDAMENTAL (pspec->value_type)) { case G_TYPE_STRING: parser = xmlNewParserCtxt(); decoded = xmlStringDecodeEntities(parser, (const xmlChar *) nodeprop, XML_SUBSTITUTE_REF, 0, 0, 0); g_value_set_string(&pval, (gchar *)decoded); g_free(decoded); xmlFreeParserCtxt(parser); break; case G_TYPE_INT: if(nodeprop) g_value_set_int(&pval, (gint)atoi((gchar *)nodeprop)); break; case G_TYPE_UINT: if(nodeprop) g_value_set_uint(&pval, (guint)atoi((gchar *)nodeprop)); break; case G_TYPE_FLOAT: g_value_set_float(&pval, (gfloat)atof((gchar *)nodeprop)); break; case G_TYPE_BOOLEAN: g_value_set_boolean(&pval, (gboolean)atoi((gchar*)nodeprop)); break; case G_TYPE_OBJECT: g_object_get(G_OBJECT(object), (const gchar *) cur->name, &prop_object, NULL); if (prop_object) { _nodes2object(prop_object, cur->children, force); g_value_set_object(&pval, prop_object); } else { g_warning ("Failed to unserialize '%s' object property. Expected to be initialized by given '%s' instance", (const gchar *) cur->name, G_OBJECT_TYPE_NAME (object)); } break; default: /* do nothing */ break; } g_object_set_property( G_OBJECT(object), (const gchar *) cur->name, &pval); g_value_unset(&pval); } else { g_warning("Undefined property '%s' for '%s'", cur->name, G_OBJECT_TYPE_NAME(object)); } } } if(nodeprop) g_free(nodeprop); if(mrp) g_object_unref(mrp); return TRUE; }