IceDrive Internal API

Recently I started using IceDrive, the web UI is clean, speed is good, price is good, for now I like it a lot!
Seems like there is no a public API which rclone can use, so we using webdav instead.
But, I noticed that the web app itself using internal API which is easy to understand and also they use the API in other apps as well.
So, maybe we can use the internal API in rclone?
The current version of the API is V1, so if they change something I assume they will change API version as well.
image


API

You can get a free account to test the API.

User data:

curl 'https://icedrive.net/API/Internal/V1/?request=user-data&sess=1' \
  -H 'authority: icedrive.net' \
  ...
  -H 'referer: https://icedrive.net/dashboard/' \
  -H 'accept-language: en-US,en-GB;q=0.9,en;q=0.8,ka;q=0.7' \
  -H 'cookie: icedrive=ice-REDACTED' \
  --compressed

Response:

{
  "id": REDACTED,
  "email": "REDACTED@REDACTED",
  "level_id": 3,
  "apiKey": "REDACTED",
  "fullName": "REDACTED",
  "level_type": "paid",
  "plan": "Pro",
  "avatar_url": REDACTED/avatar.jpg?p=REDACTED&cb=REDACTED",
  "auth_type": "session",
  "token": null,
  "error": false
}

Similarly storage stat:

{
  "error": false,
  "used": REDACTED,
  "used_human": "xx.99 GB",
  "max": REDACTED,
  "max_human": "1.00 TB",
  "free": REDACTED,
  "free_human": "xx GB",
  "pcent": xx,
  "pcent_raw": REDACTED
}

Getting main/root folders:
https://icedrive.net/API/Internal/V1/?request=collection&type=cloud&folderId=0&sess=1
returns metadata.

Returns metadata about subfolder specified by ID, returns from the prev. request:
https://icedrive.net/API/Internal/V1/?request=collection&type=cloud&folderId=folder-REDACTED&sess=1

File info:
https://icedrive.net/API/Internal/V1/?request=file-properties&id=file-REDACTED&sess=1
.etc

rename, delete(trash-add), upload, get metadata, etc can be extracted from the web app as well.

So, there is an API but needs some work to extract it.
Maybe it's worth to try

Feel free to submit an issue with a feature request but there are quite the number of new backends that get requested that just don't get enough attention to merit any work on them.

You can always work it yourself and do a PR for it as well depending on your skillset as that's beyond my skillset as well :slight_smile:

1 Like

IceDrive Internal API

We need a cookie in order to use the API, login receives e-mail and password and returns cookie, which we can use later in API:

ice_drive_email - IceDrive account e-mail
ice_drive_password - IceDrive account password
ice_drive_cookie - received after login

var $API_URL = "/API/Internal/V1/";

IceRequest function can be used to POST and GET some data, we can have similar function in GO as well

// code from the web app
function IceRequest(method, type, data) {
  data = data || {};
  data.sess = 1;
  if (method == "POST") {
    var fd = new FormData();
    fd.append("request", type);
    for (var key in data) {
      fd.append(key, data[key]);
    }
    return new Promise(function (resolve, reject) {
      fetch($API_URL, { method: "post", credentials: "include", body: fd })
        .then(function (response) {
          return response.json();
        })
        .then(function (data) {
          if (data.error) reject(data);
          resolve(data);
        })
        .catch(function (error) {
          reject({ error: !0, code: 0, message: "API Error" });
        });
    });
  } else {
    var queryString = Object.keys(data)
      .map(function (key) {
        return key + "=" + data[key];
      })
      .join("&");
    var url = $API_URL + "?request=" + type + "&" + queryString;
    return new Promise(function (resolve, reject) {
      var headers = {
        Accept: "application/json",
        "Content-Type": "application/json",
        Cache: "no-cache",
      };
      fetch(url, { method: "GET", credentials: "include", headers: headers })
        .then(function (response) {
          return response.json();
        })
        .then(function (data) {
          if (data.error) {
            reject(data);
          } else {
            resolve(data);
          }
        })
        .catch(function (error) {
          reject({ error: !0, code: 0, message: "API Error" });
        });
    });
  }
}

Login:
We need to call login in order to get cookie which is used in future requests:

IceRequest("POST", "login", { email: data.email, password: data.password })

Code the app:

// code from the web app
IceRequest("POST", "login", { email: data.email, password: data.password })
  .then(function (rs) {
    if (redir) window.location.href = redir;
    else window.location.href = "/dashboard/";
  })
  .catch(function (error) {
    if (error.code == 1200 || error.code == 1112) {
      // Seems like this is called when e-mail is not confirmed
      frmApiButtonReset(data.btnText);
      modalEmailConfirm(data.email, !1);
      return;
    }
    if (error.code == 6000) {
      // When 2FA is enabled
      twoFactor(error.userId, error.method, redir);
      return;
    }
    frmApiError(error.message);
    frmApiButtonReset(data.btnText);
  });

Response:
JSON returned from login request is following:

{"error":false,"message":"Login successful","token":"ice-UUID_REDACTED","auth_data":{"id":"REDACTED","email":"REDACTED@REDACTED","level_id":"11","apiKey":"API_KEY_REDACTED","fullName":"REDACTED REDACTED","level_type":"free","plan":"Free","avatar_url":"https:\/\/icedrive.net\/assets\/avatar\/default.png","auth_type":"session","token":null}}

JSON.token is the value we are interested, if JSON.error is false.

Now, we have ice_drive_cookie to call rest of APIs.

Make sure that icedrive cookie have value JSON.token (ice_drive_cookie)


Generic User Data:
URL: https://icedrive.net/API/Internal/V1/?request=user-data&sess=1

//from app
return IceRequest("GET", "user-data", {})

Returns:

{
  "id": "REDACTED",
  "email": "REDACTED@REDACTED.REDACTED",
  "level_id": 11,
  "apiKey": "REDACTED",
  "fullName": "REDACTED REDACTED",
  "level_type": "free",
  "plan": "Free",
  "avatar_url": "https://icedrive.net/assets/avatar/default.png",
  "auth_type": "session",
  "token": null,
  "error": false
}

Storage stat:
https://icedrive.net/API/Internal/V1/?request=stats-storage&sess=1

return IceRequest("GET", "stats-storage", {})

Returns:

{
  "error": false,
  "used": 0,
  "used_human": "0.00 KB",
  "max": 10737418240,
  "max_human": "10.00 GB",
  "free": 10737418240,
  "free_human": "10.00 GB",
  "pcent": 0,
  "pcent_raw": 0
}

Get root folder - GET:

https://icedrive.net/API/Internal/V1/?request=collection&type=cloud&folderId=0&sess=1

0 is hardcoded ID number for the root folder.

Response:

{
  "error": false,
  "id": 0,
  "results": 3,
  "data": [
    {
      "id": 83476825,
      "uid": "folder-83476825",
      "filename": "abc",
      "parentId": 0,
      "moddate": 1638726469,
      "isFolder": 1,
      "filesize": 0,
      "extension": null,
      "fave": 0,
      "isPublic": 0,
      "color": 0,
      "isOwner": 1,
      "isShared": 0,
      "fileType": "folder",
      "crypto": 0,
      "thumbnail": null
    },
    {
      "id": 83476880,
      "uid": "folder-83476880",
      "filename": "bvbb",
      "parentId": 0,
      "moddate": 1638726485,
      "isFolder": 1,
      "filesize": 0,
      "extension": null,
      "fave": 0,
      "isPublic": 0,
      "color": 0,
      "isOwner": 1,
      "isShared": 0,
      "fileType": "folder",
      "crypto": 0,
      "thumbnail": null
    },
    {
      "id": 708556066,
      "uid": "file-708556066",
      "filename": "a.js",
      "parentId": 0,
      "moddate": 1638723865,
      "isFolder": 0,
      "filesize": 1287,
      "extension": "js",
      "fave": 0,
      "isPublic": 0,
      "color": null,
      "isOwner": 1,
      "isShared": 0,
      "fileType": "code",
      "crypto": 0,
      "thumbnail": null
    }
  ]
}

As you can see we getting IDs for folder and files in the directory


Simiarly we can can list any folder via uid:

https://icedrive.net/API/Internal/V1/?request=collection&type=cloud&folderId=folder-83476825&sess=1

As you can see folderId is one of the IDs from above. \

Response is same as before, but if there is no files or folders, we getting following:

{
  "error": false,
  "id": 83476825,
  "results": 0,
  "data": []
}

Folder metadata:

https://icedrive.net/API/Internal/V1/?request=folder-properties&id=folder-83476825&sess=1

Response:

{
  "error": false,
  "isFolder": 1,
  "folderId": 83476825,
  "filename": "abc",
  "moddate": 1638726469,
  "num_folders": 0,
  "num_files": 0,
  "total_size": 0,
  "isOwner": 1,
  "owner": {}
}

File metadata:

https://icedrive.net/API/Internal/V1/?request=file-properties&id=file-708556066&sess=1

Response:

{
  "error": false,
  "isFolder": 0,
  "id": 708556066,
  "filename": "a.js",
  "moddate": 1638723865,
  "total_size": 1287,
  "isOwner": 1,
  "owner": []
}

id points to folder or file we want to get details about.


Create new folder:

// data.request = folder-create
// data.type = folder-create
// data.parentId: folder-83478059 (or 0 if root)
// data.filename: aaa
// data.sess: 1
IceRequest("POST", "folder-create", data)

Response:

{
  "error": false,
  "id": 83478262,
  "folderId": 83478262,
  "name": "bbb",
  "filename": "bbb",
  "mtime": 1638727085,
  "message": "Folder created"
}

Delete folder and files:

IceRequest("POST", "trash-add", { items: items })

Form data:

request=trash-add
items=folder-xxxxxx,folder-yyyyyy
sess=1

trash-restore with same item(s) restores deleted file
erase with same data deletes items from trash bin


Moving data:

request=move
items=folder-xxxxxx
folderId=yyyyyy
sess=1

items points to comma seperated, files and folders we want to move
folderId ponts to folder where we moving files and folders


Rename folder:

request=folder_rename
filename=new_name
id=folder-xxxxxxxx
sess=1

Rename files:

request=file-rename
filename=bbb
id=file-xxxx
keep_ext=true // we may need false
sess=1

Download file:

https://icedrive.net/API/Internal/V1/?request=download-multi&items=file-xxxxx&sess=1

where file-xxxxx is a file ID
Response:

{
  "error": false,
  "urls": [
    {
      "id": 00000000, // REDACTED
      "filename": "abc.xx",
      "filesize": 1282,
      "folderId": 0,
      "moddate": 000000000, // REDACTED
      "path": "",
      "url": "https://icecube-eu-402.icedrive.io/download?p=REDACTED"
    }
  ]
}

Download a folder as a zip file:

https://icedrive.net/API/Internal/V1/?request=zip-folder&id=folder-xxxxx&sess=1

Similar response as file download


Upload a file
The first thing is to get a list of endpoints (upload_endpoints), which we can use to upload the file:

https://icedrive.net/API/Internal/V1/?request=geo-filserver-list&sess=1

Response:

{
  "error": false,
  "upload_endpoints": [
    "https://icecube-eu-284.icedrive.io/deposit?p=iLzpwLUhYoHU_k0YjGeNHz60Bl0TwiNqaHzWIiczi_.VRZBfJ9Yh7Y6kXyhNl6e6.CUEV0_OJKDI5f7DueLgx0ZVd4ynOOc6i9DIyYFS5mfjkVXR4zLFlPqhn.CoVR9sb4aMhvrdvvauOL7vnfe5hA--",
    "https://icecube-eu-305.icedrive.io/deposit?p=iLzpwLUhYoHU_k0YjGeNHz60Bl0TwiNqaHzWIiczi_.VRZBfJ9Yh7Y6kXyhNl6e6.CUEV0_OJKDI5f7DueLgx0ZVd4ynOOc6i9DIyYFS5mfjkVXR4zLFlPqhn.CoVR9sb4aMhvrdvvauOL7vnfe5hA--",
    "https://icecube-eu-302.icedrive.io/deposit?p=iLzpwLUhYoHU_k0YjGeNHz60Bl0TwiNqaHzWIiczi_.VRZBfJ9Yh7Y6kXyhNl6e6.CUEV0_OJKDI5f7DueLgx0ZVd4ynOOc6i9DIyYFS5mfjkVXR4zLFlPqhn.CoVR9sb4aMhvrdvvauOL7vnfe5hA--",
    ...
    "https://icecube-eu-282.icedrive.io/deposit?p=iLzpwLUhYoHU_k0YjGeNHz60Bl0TwiNqaHzWIiczi_.VRZBfJ9Yh7Y6kXyhNl6e6.CUEV0_OJKDI5f7DueLgx0ZVd4ynOOc6i9DIyYFS5mfjkVXR4zLFlPqhn.CoVR9sb4aMhvrdvvauOL7vnfe5hA--"
  ]
}

It's a long list of endpoinds, we can choose one and use for subsequent uploads.
One of the URL will be our Request URL.

Now we can use the URL to upload our target file
Example from the web app:

function (e) {
  if ("add" == e.data.type) {
    (userId = e.data.userId), (apiKey = e.data.apiKey);
    var r = e.data.folderId.toString().replace(/\formData/g, "");
    e.data.uploadId;
    UploadSetup({
      file: e.data.file,
      uploadId: e.data.uploadId,
      queueId: e.data.queueId,
      filename: e.data.file.name,
      folderId: r,
      server: e.data.server, // one of the endpoints
      dir_path: e.data.dir_path,
      retry: 0,
    });
  }
},
function UploadSetup(e) {
  e.server;
  var r = !!e.file.lastModified && e.file.lastModified / 1e3;
  e.startTime = Math.floor(Date.now());
  var formData = new FormData();
  if (
    (formData.append("folderId", e.folderId),
    r && formData.append("moddate", r),
    e.dir_path && formData.append("dir_path", e.dir_path),
    e.file.size <= chunkSize)
  )
    formData.append("files[]", e.file),
      UploadFile(formData, e, !1, 0)
        .then(function (r) {
          postMessage({
            type: "complete",
            overwrite: e.overwrite,
            queueId: e.queueId,
            uploadId: e.uploadId,
            fileId: r.id,
            folderId: e.folderId,
            fileObj: r.fileObj,
          });
        })
...
function UploadFile(e, r, d, t) {
  return (
    (d = d || !1),
    new Promise(function (a, o) {
      var i = 0,
        l = new XMLHttpRequest();
      l.open("POST", r.server, !0),
        d && l.setRequestHeader("Content-Range", d),
        (l.upload.onprogress = function (e) {
          progress(
            r.startTime,
            t + e.loaded,
            r.file.size,
            r.uploadId,
            e.loaded - i
          ),
            (i = e.loaded);
        }),
        (l.upload.onerror = function () {
          o("Upload Error");
        }),
        (l.upload.ontimeout = function () {
          o("timeout");
        }),
        (l.onerror = function () {
          o("Upload Error");
        }),
        (l.onload = function (e) {}),
        (l.ontimeout = function () {}),
        (l.upload.onloadend = function (e) {}),
        (l.onloadend = function () {
          if (l.status > 0) {
            try {
              var e = JSON.parse(l.response);
            } catch (e) {
              o("Server errror");
            }
            e.error ? o(e.message) : a(e);
          }
        }),
        l.send(e);
    })
  );
}

If there is something else we need, commend and I'll try to extract it

If you read around here and on github, there is a lot of resistance to using private APIs

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.