User Tools

Site Tools


linux:plowshare

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
linux:plowshare [2014/05/30 23:08] – [Get folder information] adminlinux:plowshare [2022/10/29 16:15] (current) – external edit 127.0.0.1
Line 1: Line 1:
 +====== Plowshare ======
 +plowshare is a command-line (CLI) download/upload tool for popular file sharing websites (aka file hosting provider or One-Click hoster). With plowshare, you will be able to download or upload files and manage remote folders and link deletion. It runs on Linux/BSD/Unix operating system.
 +===== download and install =====
 +<code bash>
 +git clone https://code.google.com/p/plowshare/
 +make
 +</code>
 +===== plowshare examples =====
 +==== plowdown examples ====
 +  * Download a file from RapidShare:<code bash>
 +plowdown http://www.rapidshare.com/files/86545320/Tux-Trainer_25-01-2008.rar
 +</code>
 +  * Download a file from HotFile using an account (free or premium):<code bash>
 +plowdown -a myuser:mypassword http://hotfile.com/dl/68261330/2f2926f/
 +</code>Note: Don't forget to simple quote if your credentials have got characters that bash can interpret. For example: 'matt:foo$bar' or 'matt:foo+ -bar'.
 +  * Download a file from Oron with Antigate.com service (feature added since 2012.02.01):<code bash>
 +plowdown --antigate=key http://oron.com/dw726z0ohky5
 +</code>
 +  * Download a file from Oron with Death by Captcha service (feature added since 2012.05.04):<code bash>
 +plowdown --deathbycaptcha='user:pass' http://oron.com/dw726z0ohky5
 +</code>
 +  * Download a file from RapidShare with a proxy. curl supports http_proxy and https_proxy environment variables (notice that 3128 is the default port).<code bash>
 +export http_proxy=http://xxx.xxx.xxx.xxx:80
 +plowdown http://www.rapidshare.com/files/86545320/Tux-Trainer_25-01-2008.rar
 +</code>
 +  * Download a list of links (one link per line):<code bash>
 +cat file_with_links.txt
 +# This is a comment
 +http://depositfiles.com/files/abcdefghi
 +http://www.rapidshare.com/files/86545320/Tux-Trainer_25-01-2008.rar
 +plowdown file_with_links.txt
 +</code>
 +  * Download a list of links (one link per line) commenting out (with #) those successfully downloaded:<code bash>
 +plowdown -m file_with_links.txt
 +</code>
 +  * Limit the download rate (in bytes per second). Accepted prefixes are k, K, Ki, M, m, Mi:<code bash>
 +plowdown --max-rate 900K http://www.rapidshare.com/files/86545320/Tux-Trainer_25-01-2008.rar
 +</code>
 +  * Download a password-protected link from Mediafire:<code bash>
 +plowdown -p somepassword http://www.mediafire.com/?mt0egmhietj60iy
 +</code>
 +  * Avoid never-ending downloads: limit the number of tries (for captchas) and wait delays for each link:<code bash>
 +plowdown --max-retries=20 --timeout=3600 ...
 +</code>
 +==== plowup examples ====
 +Upload
 +  * Upload a file to your RapidShare account:<code bash>
 +plowup --auth=myuser:mypassword rapidshare /path/myfile.txt
 +</code>
 +  * Upload a file to RapidShare anonymously changing uploaded file name:<code bash>
 +plowup rapidshare /path/myfile.txt:anothername.txt
 +</code>
 +  * Upload a file to TurboBit with an account (premium or free):<code bash>
 +plowup -a myuser:mypassword turbobit /path/xxx
 +</code>
 +  * Upload a bunch of files (anonymously to 2Shared):<code bash>
 +plowup 2shared /path/myphotos/*
 +Notice that only files will be sent, subdirectories will be ignored.
 +</code>
 +  * Upload a file to megashares (anonymously) + set description<code bash>
 +plowup -d 'Important document' megashares /path/myfile.tex
 +</code>
 +  * Upload a file to Zshare anonymously with a proxy.<code bash>
 +export http_proxy=http://xxx.xxx.xxx.xxx:80
 +export https_proxy=http://xxx.xxx.xxx.xxx:80
 +plowup zshare /path/myfile.txt
 +</code>
 +  * Abort slow upload (if rate is below limit during 30 seconds)<code bash>
 +plowup --min-rate 100k mediafire /path/bigfile.zip
 +</code>
 +  * Modify remote filenames (example: foobar.rar gives foobar-PLOW.rar)<code bash>
 +plowup --name='%g-PLOW.%x' mirrorcreator *.rar
 +</code>
 +==== plowlist examples ====
 +  * List links contained in a shared folder link and download them all:<code bash>
 +plowlist http://www.mediafire.com/?qouncpzfe74s9 > links.txt
 +plowdown -m links.txt
 +</code>Some hosters are handling tree folders, you must specify -R/--recursive command-line switch to plowlist for enabing recursive lisiting.
 +  * List some sendspace.com web folder. Render results for vBulletin "BB" syntax.<code bash>
 +plowlist --printf '[url=%u]%f[/url]%n' http://www.sendspace.com/folder/5njdw7
 +</code>
 +  * List links contained in a dummy web page. Render results as HTML list:<code bash>
 +plowlist --fallback --printf '<li><a href="%u">%u</a></li>%n' http://en.wikipedia.org/wiki/SI_prefix
 +</code>
 +==== real examples for mediafire ====
 +=== prepare config for mediafire ===
 +  * Disable ssl mode
 +{{:linux:mediafire-ssl.png|}}
 +  * share mediafire for everyone
 +{{:linux:mediafire-share.png|}}
 +  * config create app for developer to upload file
 +{{:linux:mediafire-developer.png|}}
 +=== plowshare commands ===
 +  * plownlist<code bash>
 +plowlist http://www.mediafire.com/folder/cirj9u226cn3d/softs
  
 +Retrieving list (mediafire): http://www.mediafire.com/folder/cirj9u226cn3d/softs
 +# AOE2.zip
 +http://www.mediafire.com/?cm8jl9p668vomba
 +# avast_free_antivirus_setup.exe
 +http://www.mediafire.com/?4nrlt3bpf8i69yz
 +# Beyond Compare 3.1.3 Build 10374 + Serial Key.rar
 +http://www.mediafire.com/?vcifnqbyg0t5dn2
 +# dropbox-2-4-6.zip
 +http://www.mediafire.com/?e6133i8i3j5i8w8
 +# FshareSetup_4.7.0.exe
 +http://www.mediafire.com/?emtdd7lei7dqbhh
 +# Linux System Administration.pdf
 +http://www.mediafire.com/?n1i6zi2r74czy58
 +# MediaFireDesktop-0.10.50.9468-windows-PRODUCTION.exe
 +http://www.mediafire.com/?p9qqqkx1ikh66c1
 +# navicat8_mysql_en.zip
 +http://www.mediafire.com/?cntytp9ja9y642d
 +# OneDriveSetup.exe
 +http://www.mediafire.com/?os9abu5gh9qhjte
 +# Sparx Enterprise Architect v9.0.0.908.rar
 +http://www.mediafire.com/?59gu9hygmrx2pjz
 +</code>
 +  * plowndown
 +  * plownup
 +
 +===== Basic knowlege =====
 +==== Using basic commands(non script) from plowshare source ====
 +=== debug plowlist ===
 +debug below command:
 +<code bash>
 +bash -x /usr/local/bin/plowlist  http://www.mediafire.com/folder/cirj9u226cn3d/softs
 +</code>
 +=> Check curl and php call
 +  * command 1: <code bash>
 +curl --insecure --compressed --speed-time 600 --connect-timeout 240 --user-agent 'Mozilla/5.0 (X11; Linux x86_64; rv:6.0) Gecko/20100101 Firefox/6.0' --silent -d folder_key=cirj9u226cn3d http://www.mediafire.com/api/folder/get_info.php</code><code xml>
 +<?xml version="1.0" encoding="UTF-8"?>
 +<response>
 +  <action>folder/get_info</action>
 +  <folder_info>
 +    <folderkey>cirj9u226cn3d</folderkey>
 +    <name>softs</name>
 +    <description/>
 +    <tags/>
 +    <created>2014-05-27 20:55:54</created>
 +    <privacy>public</privacy>
 +    <file_count>10</file_count>
 +    <folder_count>2</folder_count>
 +    <revision>294</revision>
 +    <owner_name>Anh Vo</owner_name>
 +    <avatar>
 +    http://www.mediafire.com/images/icons/myfiles/default.png
 +    </avatar>
 +    <flag>0</flag>
 +  </folder_info>
 +  <result>Success</result>
 +  <current_api_version>2.14</current_api_version>
 +</response>
 +</code>
 +  * command 2: <code bash>
 +curl --insecure --compressed --speed-time 600 --connect-timeout 240 --user-agent 'Mozilla/5.0 (X11; Linux x86_64; rv:6.0) Gecko/20100101 Firefox/6.0' --silent -d folder_key=cirj9u226cn3d -d content_type=files -d chunk=1 http://www.mediafire.com/api/folder/get_content.php</code><code xml>
 +<?xml version="1.0" encoding="UTF-8"?>
 +<response>
 +  <action>folder/get_content</action>
 +  <folder_content>
 +    <chunk_size>100</chunk_size>
 +    <content_type>files</content_type>
 +    <chunk_number>1</chunk_number>
 +    <files>
 +    <file>
 +      <quickkey>cm8jl9p668vomba</quickkey>
 +      <hash>
 +      4d737c99c02ac5f3b81b95ec32c2d81f099c1b91f7c505f9cebad2a01f0a7598
 +      </hash>
 +      <filename>AOE2.zip</filename>
 +      <description/>
 +      <size>382775345</size>
 +      <privacy>public</privacy>
 +      <created>2014-05-29 02:35:59</created>
 +      <password_protected>no</password_protected>
 +      <mimetype>application/zip</mimetype>
 +      <filetype>archive</filetype>
 +      <view>0</view>
 +      <edit>0</edit>
 +      <revision>275</revision>
 +      <flag>0</flag>
 +      <links>
 +      <normal_download>
 +      http://www.mediafire.com/download/cm8jl9p668vomba/AOE2.zip
 +      </normal_download>
 +      </links>
 +    </file>
 +    ....................
 +    </files>
 +  </folder_content>
 +  <result>Success</result>
 +  <current_api_version>2.14</current_api_version>
 +</response>
 +</code>
 +=== debug plowdown ===
 +debug below command:
 +<code bash>
 + bash -x  /usr/local/bin/plowdown  http://www.mediafire.com/?os9abu5gh9qhjte
 +</code>
 +=> Check curl and php call
 +  * command 1: <code bash>
 +curl --insecure --compressed --speed-time 600 --connect-timeout 240 --user-agent 'Mozilla/5.0 (X11; Linux x86_64; rv:6.0) Gecko/20100101 Firefox/6.0' --silent --head 'http://www.mediafire.com/?os9abu5gh9qhjte'</code><code>
 +HTTP/1.1 301
 +Date: Fri, 30 May 2014 04:20:28 GMT
 +Content-Type: text/html; charset=utf-8
 +Connection: close
 +Cache-control: no-cache
 +Expires: 0
 +Location: /download/os9abu5gh9qhjte/OneDriveSetup.exe
 +Pragma: no-cache
 +Set-Cookie: ukey=ue25ueitucwb8fbolgu8dpn869nur89o; expires=Fri, 29-Apr-2016 04:20:28 GMT; path=/; domain=.mediafire.com; httponly
 +Server: MediaFire
 +Access-Control-Allow-Origin: *
 +</code>
 +  * command 2: <code bash>
 +curl --insecure --compressed --speed-time 600 --connect-timeout 240 --user-agent 'Mozilla/5.0 (X11; Linux x86_64; rv:6.0) Gecko/20100101 Firefox/6.0' --silent -b /tmp/plowdown.23305.18589 -c /tmp/plowdown.23305.18589 http://www.mediafire.com/download/os9abu5gh9qhjte/OneDriveSetup.exe</code>
 +==== Using basic commands base on mediafire API ====
 +refer: http://www.mediafire.com/developers/
 +=== Get Login Token ===
 +<code bash> 
 +curl -k "https://www.mediafire.com/api/user/get_login_token.php?[email protected]&password=8941362&application_id=41323&signature=8d71ce0791d93d9192800b20b1b5aceb486534c2&version=2"
 +</code>
 +<code xml>
 +<login_token>7pbzcy6sm6hnzbfgdxxtt5nn610a1k8k4j2i9t5f11f6a99rikcpt19zpaq14ta6</login_token>
 +</code>
 +=== Get Session Token ===
 +<code bash>
 +curl -k "https://www.mediafire.com/api/user/get_session_token.php?[email protected]&password=xxxxxxx&application_id=41323&signature=8d71ce0791d93d9192800b20b1b5aceb486534c2&version=2"
 +</code>
 +<code xml>
 +<response>
 +  <action>user/get_session_token</action>
 +  <session_token>
 +b781767140c809f2c7fea45275161504ac29a545ee4d451d4e25f075de67c45f93b22945df3eaf005fa92436ca80a496eab36332f5d401a84a6e31e2448c02606da9c23fce60a5bb
 +  </session_token>
 +  <pkey>5c83209e7f</pkey>
 +  <result>Success</result>
 +  <current_api_version>2.14</current_api_version>
 +</response>
 +</code>
 +=== Get mediafire setting ===
 +<code bash>
 +curl "http://www.mediafire.com/api/user/get_settings.php?session_token=b781767140c809f2c7fea45275161504ac29a545ee4d451d4e25f075de67c45f93b22945df3eaf005fa92436ca80a496eab36332f5d401a84a6e31e2448c02606da9c23fce60a5bb"
 +</code>
 +<code xml>
 +<?xml version="1.0" encoding="UTF-8"?>
 +<response>
 +  <action>user/get_settings</action>
 +  <settings>
 +    <max_upload_size>21474836580</max_upload_size>
 +    <max_instant_upload_size>21474836580</max_instant_upload_size>
 +    <validated>yes</validated>
 +    <instant_uploads_enabled>yes</instant_uploads_enabled>
 +    <show_download_page>
 +    <me_from_me>no</me_from_me>
 +    <me_from_others>yes</me_from_others>
 +    <others_from_me>no</others_from_me>
 +    </show_download_page>
 +    <used_storage_size>3192776282</used_storage_size>
 +    <storage_limit>12886999040</storage_limit>
 +    <storage_limit_exceeded>no</storage_limit_exceeded>
 +    <previous_file_versions>10</previous_file_versions>
 +    <default_share_link_status>inherit</default_share_link_status>
 +  </settings>
 +  <result>Success</result>
 +  <current_api_version>2.14</current_api_version>
 +</response
 +</code>
 +=== Get folder information ===
 +<code bash>
 +curl "http://www.mediafire.com/api/folder/get_content.php?folder_key=cirj9u226cn3d&session_token=b781767140c809f2c7fea45275161504ac29a545ee4d451d4e25f075de67c45f93b22945df3eaf005fa92436ca80a496eab36332f5d401a84a6e31e2448c02606da9c23fce60a5bb&content_type=folders"
 +</code>
 +<code xml>
 +<?xml version="1.0" encoding="UTF-8"?>
 +<response>
 +  <action>folder/get_content</action>
 +  <folder_content>
 +    <chunk_size>100</chunk_size>
 +    <content_type>folders</content_type>
 +    <chunk_number>1</chunk_number>
 +    <folders>
 +    <folder>
 +    <folderkey>obj7d1qatoaop</folderkey>
 +    <name>designer</name>
 +    <description/>
 +    <tags/>
 +    <privacy>public</privacy>
 +    <created>2014-05-28 18:41:34</created>
 +    <revision>266</revision>
 +    <flag>2</flag>
 +    <file_count>15</file_count>
 +    <folder_count>0</folder_count>
 +    <dropbox_enabled>no</dropbox_enabled>
 +    </folder>
 +    </folders>
 +  </folder_content>
 +  <result>Success</result>
 +  <current_api_version>2.14</current_api_version>
 +</response>
 +</code>
 +=== Get list files in folder ===
 +<code bash>
 +curl "http://www.mediafire.com/api/folder/get_content.php?folder_key=cirj9u226cn3d&session_token=b781767140c809f2c7fea45275161504ac29a545ee4d451d4e25f075de67c45f93b22945df3eaf005fa92436ca80a496eab36332f5d401a84a6e31e2448c02606da9c23fce60a5bb&content_type=files"
 +</code>
 +<?xml version="1.0" encoding="UTF-8"?>
 +<response>
 +  <action>folder/get_content</action>
 +  <folder_content>
 +    <chunk_size>100</chunk_size>
 +    <content_type>files</content_type>
 +    <chunk_number>1</chunk_number>
 +    <files>
 +      <file>
 +        <quickkey>4nrlt3bpf8i69yz</quickkey>
 +        <hash>
 +        ed34cb6a33372502ef61aa949fc58fe643a1d8cf830e2a455ab4bcb49759ceda
 +        </hash>
 +        <filename>avast_free_antivirus_setup.exe</filename>
 +        <description/>
 +        <size>94714880</size>
 +        <privacy>public</privacy>
 +        <created>2014-05-27 20:53:58</created>
 +        <password_protected>no</password_protected>
 +        <mimetype>application/x-dosexec</mimetype>
 +        <filetype>application</filetype>
 +        <view>0</view>
 +        <edit>0</edit>
 +        <revision>271</revision>
 +        <flag>2</flag>
 +        <downloads>0</downloads>
 +        <views>0</views>
 +        <links>
 +        <normal_download>
 +        http://www.mediafire.com/download/4nrlt3bpf8i69yz/avast_free_antivirus_setup.exe
 +        </normal_download>
 +        </links>
 +      </file>
 +      <file>
 +        <quickkey>vcifnqbyg0t5dn2</quickkey>
 +        <hash>
 +        0b7c1953ef9aa4e396dfef944b392d7ae094ffb781c121b89d0e19ee01c6eb8b
 +        </hash>
 +        <filename>Beyond Compare 3.1.3 Build 10374 + Serial Key.rar</filename>
 +        <description/>
 +        <size>5680787</size>
 +        <privacy>public</privacy>
 +        <created>2012-05-20 21:05:56</created>
 +        <password_protected>no</password_protected>
 +        <mimetype>application/x-rar</mimetype>
 +        <filetype>archive</filetype>
 +        <view>0</view>
 +        <edit>0</edit>
 +        <revision>289</revision>
 +        <flag>2</flag>
 +        <downloads>1</downloads>
 +        <views>0</views>
 +        <links>
 +        <normal_download>
 +        http://www.mediafire.com/download/vcifnqbyg0t5dn2/Beyond_Compare_3.1.3_Build_10374_+_Serial_Key.rar
 +        </normal_download>
 +        </links>
 +      </file>
 +    .................
 +    </files>
 +  </folder_content>
 +  <result>Success</result>
 +  <current_api_version>2.14</current_api_version>
 +</response>
 +<code xml>
 +</code>
 +=== Get direct download link base on quickey ===
 +<code bash>
 +curl "http://www.mediafire.com/api/file/get_links.php?link_type=direct_download&session_token=b781767140c809f2c7fea45275161504ac29a545ee4d451d4e25f075de67c45f93b22945df3eaf005fa92436ca80a496eab36332f5d401a84a6e31e2448c02606da9c23fce60a5bb&quick_key=59gu9hygmrx2pjz&response_format=xml"
 +</code>
 +<code xml>
 +<?xml version="1.0" encoding="UTF-8"?>
 +<response>
 +  <action>file/get_links</action>
 +  <links>
 +    <link>
 +      <quickkey>59gu9hygmrx2pjz</quickkey>
 +      <direct_download>      http://download646.mediafire.com/8fhslcdsebjg/59gu9hygmrx2pjz/Sparx+Enterprise+Architect+v9.0.0.908.rar
 +      </direct_download>
 +    </link>
 +  </links>
 +  <direct_download_free_bandwidth>46</direct_download_free_bandwidth>
 +  <result>Success</result>
 +  <current_api_version>2.14</current_api_version>
 +</response>
 +</code>
 +=== add web upload ===
 +<code bash>
 +curl "http://www.mediafire.com/api/upload/add_web_upload.php?session_token=b781767140c809f2c7fea45275161504ac29a545ee4d451d4e25f075de67c45f93b22945df3eaf005fa92436ca80a496eab36332f5d401a84a6e31e2448c02606da9c23fce60a5bb&url=http%3A%2F%2Fvn.easyvn.biz%2Ffiles%2Fsoftware%2F2013%2F08%2Facdsee-pro-6-3-build-221.zip&filename=acdsee-pro-6-3-build-221.zip"
 +</code>
 +<code xml>
 +<?xml version="1.0" encoding="UTF-8"?>
 +<response>
 +  <action>upload/add_web_upload</action>
 +  <upload_key>sxs36umq1d</upload_key>
 +  <result>Success</result>
 +  <current_api_version>2.14</current_api_version>
 +</response>
 +</code>
 +
 +==== make file for install ====
 +===== plowshare core =====
 +==== post_login ====
 +<code bash>
 +post_login() {
 +    local -r AUTH=$1
 +    local -r COOKIE=$2
 +    local -r POSTDATA=$3
 +    local -r LOGIN_URL=$4
 +    shift 4
 +    local -a CURL_ARGS=("$@")
 +    local USER PASSWORD DATA RESULT
 +
 +    if [ -z "$AUTH" ]; then
 +        log_error "$FUNCNAME: authentication string is empty"
 +        return $ERR_LOGIN_FAILED
 +    fi
 +
 +    if [ -z "$COOKIE" ]; then
 +        log_error "$FUNCNAME: cookie file expected"
 +        return $ERR_LOGIN_FAILED
 +    fi
 +
 +    # Seem faster than
 +    # IFS=":" read -r USER PASSWORD <<< "$AUTH"
 +    USER=$(echo "${AUTH%%:*}" | uri_encode_strict)
 +    PASSWORD=$(echo "${AUTH#*:}" | uri_encode_strict)
 +
 +    if [ -z "$PASSWORD" -o "$AUTH" = "${AUTH#*:}" ]; then
 +        PASSWORD=$(prompt_for_password) || true
 +    fi
 +
 +    log_notice "Starting login process: $USER/${PASSWORD//?/*}"
 +
 +    DATA=$(eval echo "${POSTDATA//&/\\&}")
 +    RESULT=$(curl --cookie-jar "$COOKIE" --data "$DATA" "${CURL_ARGS[@]}" \
 +        "$LOGIN_URL") || return
 +
 +    # "$RESULT" can be empty, this is not necessarily an error
 +    if [ ! -s "$COOKIE" ]; then
 +        log_debug "$FUNCNAME: no entry was set (empty cookie file)"
 +        return $ERR_LOGIN_FAILED
 +    fi
 +
 +    log_report '=== COOKIE BEGIN ==='
 +    logcat_report "$COOKIE"
 +    log_report '=== COOKIE END ==='
 +
 +    if ! find_in_array CURL_ARGS[@] '-o' '--output'; then
 +        echo "$RESULT"
 +    fi
 +}
 +</code>
 +==== parse ====
 +<code bash>
 +# Get lines that match filter+parse regular expressions and extract string from it.
 +#
 +# $1: regexp to filter (take lines matching $1 pattern; "." or "" disable filtering).
 +# $2: regexp to parse (must contain parentheses to capture text). Example: "url:'\(http.*\)'"
 +# $3: (optional) how many lines to skip (default is 0: filter and match regexp on same line).
 +#     Note: $3 may only be used if line filtering is active ($1 != ".")
 +#     Example ($3=1): get lines matching filter regexp, then apply parse regexp on the line after.
 +#     Example ($3=-1): get lines matching filter regexp, then apply parse regexp on the line before.
 +# stdin: text data
 +# stdout: result
 +parse_all() {
 +    local PARSE=$2
 +    local -i N=${3:-0}
 +    local -r D=$'\001' # Change sed separator to allow '/' characters in regexp
 +    local STRING FILTER
 +
 +    if [ -n "$1" -a "$1" != '.' ]; then
 +        FILTER="\\${D}$1${D}" # /$1/
 +    else
 +        [ $N -eq 0 ] || return $ERR_FATAL
 +    fi
 +
 +    [ '^' = "${PARSE:0:1}" ] || PARSE="^.*$PARSE"
 +    [ '$' = "${PARSE:(-1):1}" ] || PARSE+='.*$'
 +    PARSE="s${D}$PARSE${D}\1${D}p" # s/$PARSE/\1/p
 +
 +    if [ $N -eq 0 ]; then
 +        # STRING=$(sed -ne "/$1/ s/$2/\1/p")
 +        STRING=$(sed -ne "$FILTER $PARSE")
 +
 +    elif [ $N -eq 1 ]; then
 +        # Note: Loop (with label) is required for consecutive matches
 +        # STRING=$(sed -ne ":a /$1/ {n;h; s/$2/\1/p; g;ba;}")
 +        STRING=$(sed -ne ":a $FILTER {n;h; $PARSE; g;ba;}")
 +
 +    elif [ $N -eq -1 ]; then
 +        # STRING=$(sed -ne "/$1/ {x; s/$2/\1/p; b;}" -e 'h')
 +        STRING=$(sed -ne "$FILTER {x; $PARSE; b;}" -e 'h')
 +
 +    else
 +        local -r FIRST_LINE='^\([^\n]*\).*$'
 +        local -r LAST_LINE='^.*\n\(.*\)$'
 +        local N_ABS=$(( N < 0 ? -N : N ))
 +        local I=$(( N_ABS - 2 )) # Note: N_ABS >= 2 due to "elif" above
 +        local LINES='.*'
 +        local INIT='N'
 +        local FILTER_LINE PARSE_LINE
 +
 +        [ $N_ABS -gt 10 ] &&
 +            log_notice "$FUNCNAME: are you sure you want to skip $N lines?"
 +
 +        while (( I-- )); do
 +            INIT+=';N'
 +        done
 +
 +        while (( N_ABS-- )); do
 +            LINES+='\n.*'
 +        done
 +
 +        if [ $N -gt 0 ]; then
 +            FILTER_LINE=$FIRST_LINE
 +            PARSE_LINE=$LAST_LINE
 +        else
 +            FILTER_LINE=$LAST_LINE
 +            PARSE_LINE=$FIRST_LINE
 +        fi
 +
 +        STRING=$(sed -ne "1 {$INIT;h;n}" \
 +            -e "H;g;s/^.*\\n\\($LINES\)$/\\1/;h" \
 +            -e "s/$FILTER_LINE/\1/" \
 +            -e "$FILTER {g;s/$PARSE_LINE/\1/;$PARSE }")
 +
 +        # Explanation: [1], [2] let hold space always contain the current line
 +        #                       as well as the previous N lines
 +        # [3] let pattern space contain only the line we test filter regex
 +        #     on (i.e. first buffered line on skip > 0, last line on skip < 0)
 +        # [4] if filter regex matches, let pattern space contain the line to
 +        #     be parsed and apply parse command
 +    fi
 +
 +    if [ -z "$STRING" ]; then
 +        log_error "$FUNCNAME failed (sed): \"/$1/ ${PARSE//$D//}\" (skip $N)"
 +        log_notice_stack
 +        return $ERR_FATAL
 +    fi
 +
 +    echo "$STRING"
 +}
 +
 +# Like parse_all, but get only first match
 +parse() {
 +    local PARSE=$2
 +    local -i N=${3:-0}
 +    local -r D=$'\001' # Change sed separator to allow '/' characters in regexp
 +    local STRING FILTER
 +
 +    if [ -n "$1" -a "$1" != '.' ]; then
 +        FILTER="\\${D}$1${D}" # /$1/
 +    else
 +        [ $N -eq 0 ] || return $ERR_FATAL
 +    fi
 +
 +    [ '^' = "${PARSE:0:1}" ] || PARSE="^.*$PARSE"
 +    [ '$' = "${PARSE:(-1):1}" ] || PARSE+='.*$'
 +    PARSE="s${D}$PARSE${D}\1${D}p" # s/$PARSE/\1/p
 +
 +    if [ $N -eq 0 ]; then
 +        # Note: This requires GNU sed (which is assumed by Plowshare4)
 +        #STRING=$(sed -ne "$FILTER {$PARSE;ta;b;:a;q;}")
 +        STRING=$(sed -ne "$FILTER {$PARSE;T;q;}")
 +
 +    elif [ $N -eq 1 ]; then
 +        #STRING=$(sed -ne ":a $FILTER {n;h;$PARSE;tb;ba;:b;q;}")
 +        STRING=$(sed -ne ":a $FILTER {n;$PARSE;Ta;q;}")
 +
 +    elif [ $N -eq -1 ]; then
 +        #STRING=$(sed -ne "$FILTER {g;$PARSE;ta;b;:a;q;}" -e 'h')
 +        STRING=$(sed -ne "$FILTER {g;$PARSE;T;q;}" -e 'h')
 +
 +    else
 +        local -r FIRST_LINE='^\([^\n]*\).*$'
 +        local -r LAST_LINE='^.*\n\(.*\)$'
 +        local N_ABS=$(( N < 0 ? -N : N ))
 +        local I=$(( N_ABS - 2 ))
 +        local LINES='.*'
 +        local INIT='N'
 +        local FILTER_LINE PARSE_LINE
 +
 +        [ $N_ABS -gt 10 ] &&
 +            log_notice "$FUNCNAME: are you sure you want to skip $N lines?"
 +
 +        while (( I-- )); do
 +            INIT+=';N'
 +        done
 +
 +        while (( N_ABS-- )); do
 +            LINES+='\n.*'
 +        done
 +
 +        if [ $N -gt 0 ]; then
 +            FILTER_LINE=$FIRST_LINE
 +            PARSE_LINE=$LAST_LINE
 +        else
 +            FILTER_LINE=$LAST_LINE
 +            PARSE_LINE=$FIRST_LINE
 +        fi
 +
 +        # Note: Need to "clean" conditionnal flag after s/$PARSE_LINE/\1/
 +        STRING=$(sed -ne "1 {$INIT;h;n}" \
 +            -e "H;g;s/^.*\\n\\($LINES\)$/\\1/;h" \
 +            -e "s/$FILTER_LINE/\1/" \
 +            -e "$FILTER {g;s/$PARSE_LINE/\1/;ta;:a;$PARSE;T;q;}")
 +    fi
 +
 +    if [ -z "$STRING" ]; then
 +        log_error "$FUNCNAME failed (sed): \"/$1/ ${PARSE//$D//}\" (skip $N)"
 +        log_notice_stack
 +        return $ERR_FATAL
 +    fi
 +
 +    echo "$STRING"
 +}
 +</code>
 +==== parse_json ====
 +<code bash>
 +# Simple and limited JSON parsing
 +#
 +# Notes:
 +# - Single line parsing oriented (user should strip newlines first): no tree model
 +# - Array and Object types: basic poor support (depth 1 without complex types)
 +# - String type: no support for escaped unicode characters (\uXXXX)
 +# - No non standard C/C++ comments handling (like in JSONP)
 +# - If several entries exist on same line: last occurrence is taken, but:
 +#   consider precedence (order of priority): number, boolean/empty, string.
 +# - If several entries exist on different lines: all are returned (it's a parse_all_json)
 +#
 +# $1: variable name (string)
 +# $2: (optional) preprocess option. Accepted values are:
 +#     - "join": make a single line of input stream.
 +#     - "split": split input buffer on comma character (,).
 +# stdin: JSON data
 +# stdout: result
 +parse_json() {
 +    local -r NAME="\"$1\"[[:space:]]*:[[:space:]]*"
 +    local STRING PRE
 +    local -r END='\([,}[:space:]].*\)\?$'
 +
 +    if [ "$2" = 'join' ]; then
 +        PRE="tr -d '\n\r'"
 +    elif [ "$2" = 'split' ]; then
 +        PRE=sed\ -e\ 's/,[[:space:]]*\(["{]\)/\n\1/g'
 +    else
 +        PRE='cat'
 +    fi
 +
 +    # Note: "ta;:a" is a trick for cleaning conditionnal flag
 +    STRING=$($PRE | sed \
 +        -ne "/$NAME\[/{s/^.*$NAME\(\[[^]]*\]\).*$/\1/;ta;:a;s/^\[.*\[//;t;p;q;}" \
 +        -ne "/$NAME{/{s/^.*$NAME\({[^}]*}\).*$/\1/;ta;:a;s/^{.*{//;t;p;q;}" \
 +        -ne "s/^.*$NAME\(-\?\(0\|[1-9][[:digit:]]*\)\(\.[[:digit:]]\+\)\?\([eE][-+]\?[[:digit:]]\+\)\?\)$END/\1/p" \
 +        -ne "s/^.*$NAME\(true\|false\|null\)$END/\1/p" \
 +        -ne "s/\\\\\"/\\\\q/g;s/^.*$NAME\"\([^\"]*\)\"$END/\1/p")
 +
 +    if [ -z "$STRING" ]; then
 +        log_error "$FUNCNAME failed (json): \"$1\""
 +        log_notice_stack
 +        return $ERR_FATAL
 +    fi
 +
 +    # Translate two-character sequence escape representations
 +    STRING=${STRING//\\\//\/}
 +    STRING=${STRING//\\\\/\\}
 +    STRING=${STRING//\\q/\"}
 +    STRING=${STRING//\\b/$'\b'}
 +    STRING=${STRING//\\f/$'\f'}
 +    STRING=${STRING//\\n/$'\n'}
 +    STRING=${STRING//\\r/$'\r'}
 +    STRING=${STRING//\\t/ }
 +
 +    echo "$STRING"
 +}
 +</code>
 +===== plowshare for mediafire =====
 +==== mediafire login ====
 +=== mediafire web ===
 +== login form ==
 +<code html>
 +<div id="loginStep1"> 
 +  <div class="popup-title">How do you want to log in?</div>  
 +  <div id="emailLogin" class="left"> 
 +    <div class="errorBox">Invalid email or password.</div> 
 +    <form id="form_login1" id="form_login1" method="post" action="/dynamic/client_login/mediafire.php" target="widgetwork"> 
 +      <label class="ieLabel">Email address</label> 
 +      <input type="email" pattern="^(.)+@[A-Za-z0-9]([A-Za-z0-9\.-]*[A-Za-z0-9])?\.[A-Za-z]{2,13}$" name="login_email" id="widget_login_email" placeholder="Email address"/>
 +      <label class="ieLabel">Password</label> 
 +      <input type="password" name="login_pass" id="widget_login_pass" placeholder="Password" maxlength="30"/> 
 +      <div id="login_remember_wrap">
 +        <input type="checkbox" name="login_remember" id="login_remember" checked="checked"/>
 +        <label for="login_remember">Keep me logged in</label>
 +      </div> 
 +      <a class="forgotPassword" href="/lost_password.php" target="_blank">Forgot password?</a>
 +      <button type="submit" class="gbtnTertiary" onclick="">Log in</button>
 +    </form>
 +</div>
 +</code>
 +=> post url: /dynamic/client_login/mediafire.php
 +== debug post login send ==
 +  * Header<code>
 +(Request-Line) POST /dynamic/client_login/mediafire.php HTTP/1.1
 +Host www.mediafire.com
 +</code>
 +  * Post data<code>
 +login_email [email protected]
 +login_pass xxxxxxx
 +login_remember on
 +</code>
 +== debug reponse code for login ==
 +compare reponse OK and response fail
 +{{:linux:mediafire-reponselogin.png|}}\\
 +  var et= 15
 +=> Login OK\\
 +  var fp='[email protected]';
 +=> username
 +=== mediafire login code ===
 +<code bash>
 +mediafire_download() {
 +    local -r COOKIE_FILE=$1
 +    local -r BASE_URL='http://www.mediafire.com'
 +    local FILE_ID URL PAGE JSON JS_VAR
 +
 +    if [ -n "$AUTH_FREE" ]; then
 +        mediafire_login "$AUTH_FREE" "$COOKIE_FILE" "$BASE_URL" || return
 +    fi
 +    .................
 +}
 +# Static function. Proceed with login
 +# $1: authentication
 +# $2: cookie file
 +# $3: base URL
 +mediafire_login() {
 +    local -r AUTH_FREE=$1
 +    local -r COOKIE_FILE=$2
 +    local -r BASE_URL=$3
 +    local -r ENC_BASE_URL=$(uri_encode_strict <<< "$BASE_URL/")
 +    local LOGIN_DATA PAGE CODE NAME
 +
 +    # Make sure we have "ukey" cookie (mandatory)
 +    curl -c "$COOKIE_FILE" -o /dev/null "$BASE_URL"
 +
 +    # Notes: - "login_remember=on" not required
 +    #        - force SSLv3 to avoid problems with curl using OpenSSL/1.0.1
 +    LOGIN_DATA='login_email=$USER&login_pass=$PASSWORD&submit_login=Login+to+MediaFire'
 +    PAGE=$(post_login "$AUTH_FREE" "$COOKIE_FILE" "$LOGIN_DATA" \
 +        "${BASE_URL/#http/https}/dynamic/login.php?popup=1" \
 +        -b "$COOKIE_FILE" --sslv3 --referer "$BASE_URL") || return
 +
 +    # Note: Cookies "user" and "session" get set on successful login, "skey" is changed"
 +    CODE=$(echo "$PAGE" | parse 'var et' 'var et= \(-\?[[:digit:]]\+\);') || return
 +    NAME=$(echo "$PAGE" | parse 'var fp' "var fp='\([^']\+\)';") || return
 +
 +    # Check for errors
 +    # Note: All error codes are explained in page returned by server.
 +    if [ $CODE -ne 15 ]; then
 +        log_debug "Remote error: $ERR"
 +        return $ERR_LOGIN_FAILED
 +    fi
 +
 +    log_debug "Successfully logged in as member '$NAME'"
 +}
 +</code>
 +==== mediafire download ====
 +  * download.sh<code bash>
 +local FUNCTION=${MODULE}_download
 +$FUNCTION "$COOKIE_FILE" "$URL_ENCODED" >"$DRESULT" || DRETVAL=$?
 +</code>
 +  * mediafire.sh<code bash>
 +# Output a mediafire file download URL
 +# $1: cookie file
 +# $2: mediafire.com url
 +# stdout: real file download link
 +mediafire_download() {
 +    local -r COOKIE_FILE=$1
 +    local -r BASE_URL='http://www.mediafire.com'
 +    local FILE_ID URL PAGE JSON JS_VAR
 +
 +    if [ -n "$AUTH_FREE" ]; then
 +        mediafire_login "$AUTH_FREE" "$COOKIE_FILE" "$BASE_URL" || return
 +    fi
 +
 +    FILE_ID=$(mediafire_extract_id "$2") || return
 +
 +    if ! mediafire_is_file_id "$FILE_ID"; then
 +        log_error 'This is a folder link. Please use plowlist!'
 +        return $ERR_FATAL
 +    fi
 +
 +    # Only get site headers first to capture direct download links
 +    URL=$(curl --head "$BASE_URL/?$FILE_ID" | grep_http_header_location_quiet) || return
 +
 +    case "$URL" in
 +        # no redirect, normal download
 +        '')
 +            URL="$BASE_URL/?$FILE_ID"
 +            ;;
 +        /download/*)
 +            URL="$BASE_URL$URL"
 +            ;;
 +        http://*)
 +            log_debug 'Direct download'
 +            echo "$URL"
 +            return 0
 +            ;;
 +        *errno=999)
 +            return $ERR_LINK_NEED_PERMISSIONS
 +            ;;
 +        *errno=320|*errno=378)
 +            return $ERR_LINK_DEAD
 +            ;;
 +        *errno=*)
 +            log_error "Unexpected remote error: ${URL#*errno=}"
 +            return $ERR_FATAL
 +    esac
 +
 +    PAGE=$(curl -b "$COOKIE_FILE" -c "$COOKIE_FILE" "$URL" | break_html_lines) || return
 +
 +    # <h3 class="error_msg_title">Invalid or Deleted File.</h3>
 +    match 'Invalid or Deleted File' "$PAGE" && return $ERR_LINK_DEAD
 +
 +    # handle captcha (reCaptcha or SolveMedia) if there is one
 +    if match '<form[^>]*form_captcha' "$PAGE"; then
 +        local FORM_CAPTCHA PUBKEY CHALLENGE ID RESP CAPTCHA_DATA
 +
 +        FORM_CAPTCHA=$(grep_form_by_name "$PAGE" 'form_captcha') || return
 +
 +        if match 'recaptcha/api' "$FORM_CAPTCHA"; then
 +            log_debug 'reCaptcha found'
 +
 +            local WORD
 +            PUBKEY='6LextQUAAAAAALlQv0DSHOYxqF3DftRZxA5yebEe'
 +            RESP=$(recaptcha_process $PUBKEY) || return
 +            { read WORD; read CHALLENGE; read ID; } <<< "$RESP"
 +
 +            CAPTCHA_DATA="-d recaptcha_challenge_field=$CHALLENGE -d recaptcha_response_field=$WORD"
 +
 +        elif match 'api\.solvemedia' "$FORM_CAPTCHA"; then
 +            log_debug 'Solve Media CAPTCHA found'
 +
 +            PUBKEY='Z94dMnWequbvKmy-HchLrZJ3-.qB6AJ1'
 +            RESP=$(solvemedia_captcha_process $PUBKEY) || return
 +            { read CHALLENGE; read ID; } <<< "$RESP"
 +
 +            CAPTCHA_DATA="--data-urlencode adcopy_challenge=$CHALLENGE -d adcopy_response=manual_challenge"
 +
 +        else
 +            log_error 'Unexpected content/captcha type. Site updated?'
 +            return $ERR_FATAL
 +        fi
 +
 +        log_debug "Captcha data: $CAPTCHA_DATA"
 +
 +        PAGE=$(curl --location -b "$COOKIE_FILE" --referer "$URL" \
 +            $CAPTCHA_DATA "$BASE_URL/?$FILE_ID") || return
 +
 +        # Your entry was incorrect, please try again!
 +        if match 'Your entry was incorrect' "$PAGE"; then
 +            captcha_nack $ID
 +            log_error 'Wrong captcha'
 +            return $ERR_CAPTCHA
 +        fi
 +
 +        captcha_ack $ID
 +        log_debug 'Correct captcha'
 +    fi
 +
 +    # Check for password protected link
 +    if match 'name="downloadp"' "$PAGE"; then
 +        log_debug 'File is password protected'
 +        if [ -z "$LINK_PASSWORD" ]; then
 +            LINK_PASSWORD=$(prompt_for_password) || return
 +        fi
 +        PAGE=$(curl -L --post301 -b "$COOKIE_FILE" \
 +            -d "downloadp=$LINK_PASSWORD" "$URL" | break_html_lines) || return
 +
 +        match 'name="downloadp"' "$PAGE" && return $ERR_LINK_PASSWORD_REQUIRED
 +    fi
 +
 +    JS_VAR=$(echo "$PAGE" | parse 'function[[:space:]]*_' '"\([^"]\+\)";' 1) || return
 +
 +    # extract + output download link + file name
 +    mediafire_get_ofuscated_link "$JS_VAR" | parse_attr href || return
 +    if ! parse_attr 'og:title' 'content' <<< "$PAGE"; then
 +        parse_tag 'title' <<< "$PAGE" || return
 +    fi
 +}
 +
 +</code>
 +==== mediafire upload ====
 +Refer: https://www.mediafire.com/developers/upload.php
 +=== upload code ===
 +  * [upload.sh]<code bash>
 +FUNCTION=${MODULE}_upload
 +$FUNCTION "$UCOOKIE" "$LOCALFILE" \
 +            "$DESTFILE" >"$URESULT" || URETVAL=$?
 +</code>
 +  * [mediafire.sh]<code bash>
 +# Upload a file to mediafire using official API.
 +# https://www.mediafire.com/developers/upload.php
 +# $1: cookie file (unused here)
 +# $2: input file (with full path)
 +# $3: remote filename
 +# stdout: mediafire.com download link
 +mediafire_upload() {
 +    local -r COOKIE_FILE=$1
 +    local -r FILE=$2
 +    local -r DEST_FILE=$3
 +    local -r BASE_URL='https://www.mediafire.com'
 +    local SESSION_TOKEN JSON RES KEY_ID UPLOAD_KEY QUICK_KEY FOLDER_KEY
 +
 +    # Sanity checks
 +    [ -n "$AUTH_FREE" ] || return $ERR_LINK_NEED_PERMISSIONS
 +
 +    if [ -n "$ASYNC" ] && ! match_remote_url "$FILE"; then
 +        log_error 'Cannot upload local files asynchronously.'
 +        return $ERR_BAD_COMMAND_LINE
 +    fi
 +
 +    if [ -n "$ASYNC" -a \( -n "$DESCRIPTION" -o -n "$LINK_PASSWORD" -o \
 +        -n "$PRIVATE_FILE" \) ] ; then
 +        log_error 'Advanced options not available for asynchronously uploaded files.'
 +        return $ERR_BAD_COMMAND_LINE
 +    fi
 +
 +    # FIXME
 +    if [ -z "$ASYNC" ] && match_remote_url "$FILE"; then
 +        log_error 'Synchronous remote upload not implemented.'
 +        return $ERR_BAD_COMMAND_LINE
 +    fi
 +
 +    SESSION_TOKEN=$(mediafire_api_get_session_token "$AUTH_FREE" "$BASE_URL") || return
 +    log_debug "Session Token: '$SESSION_TOKEN'"
 +
 +    # API bug
 +    if [ "${#DEST_FILE}" -lt 3 ]; then
 +        log_error 'Filenames less than 3 characters cannot be uploaded. Mediafire API bug? This is not a plowshare bug!'
 +    fi
 +
 +    if [ -n "$FOLDER" ]; then
 +        FOLDER_KEY=$(mediafire_check_folder "$SESSION_TOKEN" "$BASE_URL" "$FOLDER") || return
 +    fi
 +
 +    # Check for duplicate name
 +    JSON=$(curl --get -d "session_token=$SESSION_TOKEN" -d "filename=$DEST_FILE" \
 +        -d 'response_format=json' \
 +        -d 'action_on_duplicate=keep' \
 +         ${FOLDER:+-d "upload_folder_key=$FOLDER_KEY"} \
 +        "$BASE_URL/api/upload/pre_upload.php") || return
 +
 +    RES=$(parse_json result <<<"$JSON") || return
 +    if [ "$RES" != 'Success' ]; then
 +        local NUM MSG
 +        NUM=$(parse_json_quiet error <<<"$JSON")
 +        MSG=$(parse_json_quiet message <<<"$JSON")
 +        log_error "Unexpected remote error (pre_upload): $NUM, '$MSG'"
 +        return $ERR_FATAL
 +    fi
 +
 +    # "duplicate_name":"yes","duplicate_quickkey":"2xrys3f97a9t9ce"
 +    # Note: "duplicate_name" is not always returned ???
 +    QUICK_KEY=$(parse_json_quiet 'duplicate_quickkey' <<<"$JSON") || return
 +    if [ -n "$QUICK_KEY" ]; then
 +        if [ -n "$UNIQUE_FILE" ]; then
 +            log_error 'Duplicated filename. Return original quickkey.'
 +            echo "$BASE_URL/?$QUICK_KEY"
 +            return 0
 +        else
 +            log_debug 'a file with the same filename already exists. File will be renamed.'
 +        fi
 +    fi
 +
 +    # "used_storage_size":"10438024","storage_limit":"53687091200","storage_limit_exceeded":"no"
 +    RES=$(parse_json storage_limit_exceeded <<<"$JSON") || return
 +    if [ "$RES" = 'yes' ]; then
 +       log_error 'Storage limit exceeded. Abort.'
 +       return $ERR_SIZE_LIMIT_EXCEEDED
 +    fi
 +
 +    # Start upload
 +    if match_remote_url "$FILE"; then
 +        JSON=$(curl -d "session_token=$SESSION_TOKEN" \
 +            -d "filename=$DESTFILE"                   \
 +            -d 'response_format=json'                 \
 +            --data-urlencode "url=$FILE"              \
 +            ${FOLDER:+"-d folder_key=$FOLDER_KEY"   \
 +            "$BASE_URL/api/upload/add_web_upload.php") || return
 +
 +        KEY_ID='upload_key'
 +    else
 +        local FILE_SIZE
 +        FILE_SIZE=$(get_filesize "$FILE") || return
 +
 +        JSON=$(curl_with_log -F "Filedata=@$FILE;filename=$DESTFILE" \
 +            --header "x-filename: $DEST_FILE" \
 +            --header "x-size: $FILE_SIZE" \
 +            "$BASE_URL/api/upload/upload.php?session_token=$SESSION_TOKEN&action_on_duplicate=keep&response_format=json${FOLDER:+"&uploadkey=$FOLDER_KEY"}") || return
 +
 +        KEY_ID='key'
 +    fi
 +
 +    # Check for errors
 +    RES=$(parse_json result <<<"$JSON") || return
 +    if [ "$RES" != 'Success' ]; then
 +        local NUM MSG
 +        NUM=$(parse_json_quiet error <<<"$JSON")
 +        MSG=$(parse_json_quiet message <<<"$JSON")
 +        log_error "Unexpected remote error (upload): $NUM, '$MSG'"
 +        return $ERR_FATAL
 +    fi
 +
 +    UPLOAD_KEY=$(parse_json "$KEY_ID" <<< "$JSON") || return
 +    log_debug "polling for status update (with key $UPLOAD_KEY)"
 +    QUICK_KEY=''
 +
 +    # Wait for upload to finish
 +    if match_remote_url "$FILE"; then
 +        [ -n "$ASYNC" ] && return $ERR_ASYNC_REQUEST
 +    else
 +        for N in 3 3 2 2 2; do
 +            wait $N seconds || return
 +
 +            JSON=$(curl --get -d "session_token=$SESSION_TOKEN" \
 +                -d 'response_format=json' -d "key=$UPLOAD_KEY"  \
 +                "$BASE_URL/api/upload/poll_upload.php") || return
 +
 +            RES=$(parse_json result <<<"$JSON") || return
 +            if [ "$RES" != 'Success' ]; then
 +                log_error "FIXME '$JSON'"
 +                return $ERR_FATAL
 +            fi
 +
 +            # No more requests for this key
 +            RES=$(parse_json status <<<"$JSON") || return
 +            if [ "$RES" = '99' ]; then
 +                QUICK_KEY=$(parse_json quickkey <<<"$JSON") || return
 +                break
 +            fi
 +        done
 +    fi
 +
 +    if [ -z "$QUICK_KEY" ]; then
 +        local MSG ERR
 +        MSG=$(parse_json_quiet description <<<"$JSON")
 +        ERR=$(parse_json_quiet fileerror <<<"$JSON")
 +        log_error "Bad status $RES: '$MSG'"
 +        log_debug "fileerror: '$ERR'"
 +        return $ERR_FATAL
 +    fi
 +
 +    if [ -n "$DESCRIPTION" -o -n "$PRIVATE_FILE" ]; then
 +        JSON=$(curl -d "session_token=$SESSION_TOKEN" \
 +            -d "quick_key=$QUICK_KEY" -d 'response_format=json' \
 +            ${DESCRIPTION:+-d "description=$DESCRIPTION"} \
 +            ${PRIVATE_FILE:+-d 'privacy=private'} \
 +            "$BASE_URL/api/file/update.php") || return
 +
 +        RES=$(parse_json result <<<"$JSON")
 +        if [ "$RES" != 'Success' ]; then
 +            log_error 'Could not set description/hide file.'
 +        fi
 +    fi
 +
 +    # Note: Making a file private removes its password...
 +    if [ -n "$LINK_PASSWORD" ]; then
 +        JSON=$(curl -d "session_token=$SESSION_TOKEN" \
 +            -d "quick_key=$QUICK_KEY" -d 'response_format=json' \
 +            -d "password=$LINK_PASSWORD" \
 +            "$BASE_URL/api/file/update_password.php") || return
 +
 +        RES=$(parse_json result <<<"$JSON")
 +        if [ "$RES" != 'Success' ]; then
 +            log_error 'Could not set password.'
 +        fi
 +    fi
 +
 +    echo "$BASE_URL/?$QUICK_KEY"
 +}
 +
 +</code>
 +=== Analyser code ===
 +  * Step1: Get session token using $BASE_URL/api/user/get_session_token.php <code bash>
 +SESSION_TOKEN=$(mediafire_api_get_session_token "$AUTH_FREE" "$BASE_URL") || return
 +  </code>
 +  * Step2: Check for duplicate name using $BASE_URL/api/upload/pre_upload.php<code bash>
 +JSON=$(curl --get -d "session_token=$SESSION_TOKEN" -d "filename=$DEST_FILE" \
 +  -d 'response_format=json' \
 +  -d 'action_on_duplicate=keep' \
 +   ${FOLDER:+-d "upload_folder_key=$FOLDER_KEY"} \
 +  "$BASE_URL/api/upload/pre_upload.php") || return
 +  </code>
 +  * Step3: Start upload 
 +      * using remote upload $BASE_URL/api/upload/add_web_upload.php<code bash>
 +if match_remote_url "$FILE"; then
 +  JSON=$(curl -d "session_token=$SESSION_TOKEN" \
 +      -d "filename=$DESTFILE"                   \
 +      -d 'response_format=json'                 \
 +      --data-urlencode "url=$FILE"              \
 +      ${FOLDER:+"-d folder_key=$FOLDER_KEY"   \
 +      "$BASE_URL/api/upload/add_web_upload.php") || return
 +  </code>
 +        * Or using upload file with $BASE_URL/api/upload/upload.php<code bash>
 +else
 +    local FILE_SIZE
 +    FILE_SIZE=$(get_filesize "$FILE") || return
 +
 +    JSON=$(curl_with_log -F "Filedata=@$FILE;filename=$DESTFILE" \
 +        --header "x-filename: $DEST_FILE" \
 +        --header "x-size: $FILE_SIZE" \
 +        "$BASE_URL/api/upload/upload.php?session_token=$SESSION_TOKEN&action_on_duplicate=keep&response_format=json${FOLDER:+"&uploadkey=$FOLDER_KEY"}") || return
 +
 +    KEY_ID='key'
 +fi
 +</code>