/** * Create list of infos about objects in opened directory * Return: NS_OK when list obtained, otherwise error code according * to failed operation. */ nsresult nsGIOInputStream::DoOpenDirectory() { GError *error = nullptr; GFileEnumerator *f_enum = g_file_enumerate_children(mHandle, "standard::*,time::*", G_FILE_QUERY_INFO_NONE, nullptr, &error); if (!f_enum) { nsresult rv = MapGIOResult(error); g_warning("Cannot read from directory: %s", error->message); g_error_free(error); return rv; } // fill list of file infos GFileInfo *info = g_file_enumerator_next_file(f_enum, nullptr, &error); while (info) { mDirList = g_list_append(mDirList, info); info = g_file_enumerator_next_file(f_enum, nullptr, &error); } g_object_unref(f_enum); if (error) { g_warning("Error reading directory content: %s", error->message); nsresult rv = MapGIOResult(error); g_error_free(error); return rv; } mDirOpen = true; // Sort list of file infos by using FileInfoComparator function mDirList = g_list_sort(mDirList, FileInfoComparator); mDirListPtr = mDirList; // Write base URL (make sure it ends with a '/') mDirBuf.AppendLiteral("300: "); mDirBuf.Append(mSpec); if (mSpec.get()[mSpec.Length() - 1] != '/') mDirBuf.Append('/'); mDirBuf.Append('\n'); // Write column names mDirBuf.AppendLiteral("200: filename content-length last-modified file-type\n"); // Write charset (assume UTF-8) // XXX is this correct? mDirBuf.AppendLiteral("301: UTF-8\n"); SetContentTypeOfChannel(APPLICATION_HTTP_INDEX_FORMAT); return NS_OK; }
/** * Create file stream and set mime type for channel * @param info file info used to determine mime type * @return NS_OK when file stream created successfuly, error code otherwise */ nsresult nsGIOInputStream::DoOpenFile(GFileInfo *info) { GError *error = nullptr; mStream = g_file_read(mHandle, nullptr, &error); if (!mStream) { nsresult rv = MapGIOResult(error); g_warning("Cannot read from file: %s", error->message); g_error_free(error); return rv; } const char * content_type = g_file_info_get_content_type(info); if (content_type) { char *mime_type = g_content_type_get_mime_type(content_type); if (mime_type) { if (strcmp(mime_type, APPLICATION_OCTET_STREAM) != 0) { SetContentTypeOfChannel(mime_type); } g_free(mime_type); } } else { g_warning("Missing content type."); } mBytesRemaining = g_file_info_get_size(info); // Update the content length attribute on the channel. We do this // synchronously without proxying. This hack is not as bad as it looks! mChannel->SetContentLength(mBytesRemaining); return NS_OK; }
/** * Start mount operation and wait in loop until it is finished. This method is * called from thread which is trying to read from location. */ nsresult nsGIOInputStream::MountVolume() { GMountOperation* mount_op = g_mount_operation_new(); g_signal_connect (mount_op, "ask-password", G_CALLBACK (mount_operation_ask_password), mChannel); mMountRes = MOUNT_OPERATION_IN_PROGRESS; /* g_file_mount_enclosing_volume uses a dbus request to mount the volume. Callback mount_enclosing_volume_finished is called in main thread (not this thread on which this method is called). */ g_file_mount_enclosing_volume(mHandle, G_MOUNT_MOUNT_NONE, mount_op, nullptr, mount_enclosing_volume_finished, this); mozilla::MonitorAutoLock mon(mMonitorMountInProgress); /* Waiting for finish of mount operation thread */ while (mMountRes == MOUNT_OPERATION_IN_PROGRESS) mon.Wait(); g_object_unref(mount_op); if (mMountRes == MOUNT_OPERATION_FAILED) { return MapGIOResult(mMountErrorCode); } else { return NS_OK; } }
static nsresult MapGIOResult(GError *result) { if (!result) return NS_OK; return MapGIOResult(result->code); }
/** * Read content of file or create file list from directory * @param aBuf read destination buffer * @param aCount length of destination buffer * @param aCountRead number of read characters * @return NS_OK when read successfully, NS_BASE_STREAM_CLOSED when end of file, * error code otherwise */ nsresult nsGIOInputStream::DoRead(char *aBuf, uint32_t aCount, uint32_t *aCountRead) { nsresult rv = NS_ERROR_NOT_AVAILABLE; if (mStream) { // file read GError *error = nullptr; uint32_t bytes_read = g_input_stream_read(G_INPUT_STREAM(mStream), aBuf, aCount, nullptr, &error); if (error) { rv = MapGIOResult(error); *aCountRead = 0; g_warning("Cannot read from file: %s", error->message); g_error_free(error); return rv; } *aCountRead = bytes_read; mBytesRemaining -= *aCountRead; return NS_OK; } else if (mDirOpen) { // directory read while (aCount && rv != NS_BASE_STREAM_CLOSED) { // Copy data out of our buffer uint32_t bufLen = mDirBuf.Length() - mDirBufCursor; if (bufLen) { uint32_t n = std::min(bufLen, aCount); memcpy(aBuf, mDirBuf.get() + mDirBufCursor, n); *aCountRead += n; aBuf += n; aCount -= n; mDirBufCursor += n; } if (!mDirListPtr) // Are we at the end of the directory list? { rv = NS_BASE_STREAM_CLOSED; } else if (aCount) // Do we need more data? { GFileInfo *info = (GFileInfo *) mDirListPtr->data; // Prune '.' and '..' from directory listing. const char * fname = g_file_info_get_name(info); if (fname && fname[0] == '.' && (fname[1] == '\0' || (fname[1] == '.' && fname[2] == '\0'))) { mDirListPtr = mDirListPtr->next; continue; } mDirBuf.AssignLiteral("201: "); // The "filename" field nsCString escName; nsCOMPtr<nsINetUtil> nu = do_GetService(NS_NETUTIL_CONTRACTID); if (nu && fname) { nu->EscapeString(nsDependentCString(fname), nsINetUtil::ESCAPE_URL_PATH, escName); mDirBuf.Append(escName); mDirBuf.Append(' '); } // The "content-length" field // XXX truncates size from 64-bit to 32-bit mDirBuf.AppendInt(int32_t(g_file_info_get_size(info))); mDirBuf.Append(' '); // The "last-modified" field // // NSPR promises: PRTime is compatible with time_t // we just need to convert from seconds to microseconds GTimeVal gtime; g_file_info_get_modification_time(info, >ime); PRExplodedTime tm; PRTime pt = ((PRTime) gtime.tv_sec) * 1000000; PR_ExplodeTime(pt, PR_GMTParameters, &tm); { char buf[64]; PR_FormatTimeUSEnglish(buf, sizeof(buf), "%a,%%20%d%%20%b%%20%Y%%20%H:%M:%S%%20GMT ", &tm); mDirBuf.Append(buf); } // The "file-type" field switch (g_file_info_get_file_type(info)) { case G_FILE_TYPE_REGULAR: mDirBuf.AppendLiteral("FILE "); break; case G_FILE_TYPE_DIRECTORY: mDirBuf.AppendLiteral("DIRECTORY "); break; case G_FILE_TYPE_SYMBOLIC_LINK: mDirBuf.AppendLiteral("SYMBOLIC-LINK "); break; default: break; } mDirBuf.Append('\n'); mDirBufCursor = 0; mDirListPtr = mDirListPtr->next; } } } return rv; }
/** * Start file open operation, mount volume when needed and according to file type * create file output stream or read directory content. * @return NS_OK when file or directory opened successfully, error code otherwise */ nsresult nsGIOInputStream::DoOpen() { nsresult rv; GError *error = nullptr; NS_ASSERTION(mHandle == nullptr, "already open"); mHandle = g_file_new_for_uri( mSpec.get() ); GFileInfo *info = g_file_query_info(mHandle, "standard::*", G_FILE_QUERY_INFO_NONE, nullptr, &error); if (error) { if (error->domain == G_IO_ERROR && error->code == G_IO_ERROR_NOT_MOUNTED) { // location is not yet mounted, try to mount g_error_free(error); if (NS_IsMainThread()) return NS_ERROR_NOT_CONNECTED; error = nullptr; rv = MountVolume(); if (rv != NS_OK) { return rv; } // get info again info = g_file_query_info(mHandle, "standard::*", G_FILE_QUERY_INFO_NONE, nullptr, &error); // second try to get file info from remote files after media mount if (!info) { g_warning("Unable to get file info: %s", error->message); rv = MapGIOResult(error); g_error_free(error); return rv; } } else { g_warning("Unable to get file info: %s", error->message); rv = MapGIOResult(error); g_error_free(error); return rv; } } // Get file type to handle directories and file differently GFileType f_type = g_file_info_get_file_type(info); if (f_type == G_FILE_TYPE_DIRECTORY) { // directory rv = DoOpenDirectory(); } else if (f_type != G_FILE_TYPE_UNKNOWN) { // file rv = DoOpenFile(info); } else { g_warning("Unable to get file type."); rv = NS_ERROR_FILE_NOT_FOUND; } if (info) g_object_unref(info); return rv; }