Beispiel #1
0
        public SaveInfo Save(string pageText, string summary, bool minor, WatchOptions watch)
        {
            if (string.IsNullOrEmpty(pageText) && !Page.Exists)
            {
                throw new ArgumentException("Can't save empty pages", "pageText");
            }
            // if (string.IsNullOrEmpty(summary))
            // {
            //     throw new ArgumentException("Edit summary required", "summary");
            // }
            if (Action != "edit")
            {
                throw new ApiException(this, "This page is not opened properly for editing");
            }
            if (string.IsNullOrEmpty(Page.EditToken))
            {
                throw new ApiException(this, "Edit token is needed to edit pages");
            }

            pageText = Tools.ConvertFromLocalLineEndings(pageText);

            var get = new Dictionary<string, string>
            {
                {"action", "edit"},
                {"title", Page.Title},
                {"watchlist", WatchOptionsToParam(watch)},
            };
            get.AddIfTrue(minor, "minor", null);
            get.AddIfTrue(User.IsBot, "bot", null);

            string result = HttpPost(
                get,
                new Dictionary<string, string>
                {
                    // order matters here - https://phabricator.wikimedia.org/T16210#183159
                    {"md5", MD5(pageText)},
                    {"summary", summary},
                    {"basetimestamp", Page.Timestamp},
                    {"text", pageText},
                    {"starttimestamp", Page.TokenTimestamp},
                    {"token", Page.EditToken}
                },
                ActionOptions.All);

            var xml = CheckForErrors(result, "edit");
            Reset();
            return new SaveInfo(xml);
        }
Beispiel #2
0
        public void Protect(string title, string reason, string expiry, string edit, string move, bool cascade,
            bool watch)
        {
            if (string.IsNullOrEmpty(title)) throw new ArgumentException("Page name required", "title");
            if (string.IsNullOrEmpty(reason)) throw new ArgumentException("Deletion reason required", "reason");

            // Reset();
            Action = "protect";

            if (string.IsNullOrEmpty(Page.ProtectToken))
            {
                string result = HttpGet(
                    new Dictionary<string, string>
                    {
                        {"action", "query"},
                        {"prop", "info"},
                        {"meta", "tokens"}, // Since 1.24
                        {"type", "csrf"},
                        {"intoken", "protect"}, // Pre 1.24 compat
                        {"titles", title}

                    },
                    ActionOptions.All);

                CheckForErrors(result);

                try
                {
                    XmlReader xr = CreateXmlReader(result);
                    if (xr.ReadToFollowing("tokens"))
                    {
                        Page.ProtectToken = xr.GetAttribute("csrftoken");
                    }
                    else if (!xr.ReadToFollowing("page"))
                    {
                        throw new Exception("Cannot find <page> element");
                    }
                    else
                    {
                        Page.ProtectToken = xr.GetAttribute("protecttoken");
                    }
                }
                catch (Exception ex)
                {
                    throw new BrokenXmlException(this, ex);
                }
            }

            if (Aborting) throw new AbortedException(this);

            // if page does not exist, protection (i.e. salting) requires create protection only
            string protections;

            if (Page.Exists)
                protections = "edit=" + edit + "|move=" + move;
            else
                protections = "create=" + edit;

            string expiryvalue;

            if (string.IsNullOrEmpty(expiry))
                expiryvalue = "";
            else if (Page.Exists)
                expiryvalue = expiry + "|" + expiry;
            else
                expiryvalue = expiry;

            var post = new Dictionary<string, string>
            {
                {"title", title},
                {"token", Page.ProtectToken},
                {"reason", reason},
                {"protections", protections},
            };
            post.AddIfTrue(!string.IsNullOrEmpty(expiry), "expiry", expiryvalue);
            post.AddIfTrue(cascade, "cascade", null);

            //post.AddIfTrue(User.IsBot, "bot", null);
            post.AddIfTrue(watch, "watch", null);

            var result2 = HttpPost(
                new Dictionary<string, string>
                {
                    {"action", "protect"}
                },
                post,
                ActionOptions.All);

            CheckForErrors(result2);

            Reset();
        }
Beispiel #3
0
        /// <summary>
        /// Opens the wiki page for editing
        /// </summary>
        /// <param name="title">The wiki page title</param>
        /// <param name="resolveRedirects"></param>
        /// <returns>The current content of the wiki page</returns>
        public string Open(string title, bool resolveRedirects)
        {
            if (string.IsNullOrEmpty(title))
                throw new ArgumentException("Page name required", "title");

            if (!User.IsLoggedIn)
                throw new LoggedOffException(this);

            Reset();

            // action=query&prop=info|revisions&intoken=edit&titles=Main%20Page&rvprop=timestamp|user|comment|content

            /* converttitles: API doc says "converttitles - Convert titles to other variants if necessary. Only works if the wiki's content language supports variant conversion.
               Languages that support variant conversion include gan, iu, kk, ku, shi, sr, tg, uz, zh"
             * Example with and without converttitles: zh-wiki page 龙门飞甲
             * https://zh.wikipedia.org/w/api.php?action=query&prop=info|revisions&intoken=edit&titles=龙门飞甲&rvprop=timestamp|user|comment|content
             * https://zh.wikipedia.org/w/api.php?action=query&converttitles&prop=info|revisions&intoken=edit&titles=龙门飞甲&rvprop=timestamp|user|comment|content
             If convertitles is not set, API doesn't find the page
             */
            var query = new Dictionary<string, string>
            {
                {"action", "query"},
                {"converttitles", null},
                {"prop", "info|revisions"},
                {"meta", "tokens"}, // Since 1.24
                {"type", "csrf|watch|rollback"}, // CSRF is for most actions
                {"intoken", "edit|protect|delete|move|watch"}, // Pre 1.24 compat
                {"titles", title},
                {"inprop", "protection|watched|displaytitle"},
                {"rvprop", "content|timestamp"}, // timestamp|user|comment|
                {"curtimestamp", null}
            };
            query.AddIfTrue(resolveRedirects, "redirects", null);

            string result = HttpGet(query, ActionOptions.All);

            CheckForErrors(result, "query");

            try
            {
                Page = new PageInfo(result);

                Action = "edit";
            }
            catch (Exception ex)
            {
                throw new BrokenXmlException(this, ex);
            }

            return Page.Text;
        }
Beispiel #4
0
        public void Move(string title, string newTitle, string reason, bool moveTalk, bool noRedirect, bool watch)
        {
            if (string.IsNullOrEmpty(title)) throw new ArgumentException("Page title required", "title");
            if (string.IsNullOrEmpty(newTitle)) throw new ArgumentException("Target page title required", "newTitle");
            if (string.IsNullOrEmpty(reason)) throw new ArgumentException("Page rename reason required", "reason");

            if (title == newTitle) throw new ArgumentException("Page cannot be moved to the same title");

            //Reset();
            Action = "move";

            if (!string.IsNullOrEmpty(Page.MoveToken))
            {
                string result = HttpGet(
                    new Dictionary<string, string>
                    {
                        {"action", "query"},
                        {"prop", "info"},
                        {"meta", "tokens"}, // Since 1.24
                        {"type", "csrf"},
                        {"intoken", "move"}, // Pre 1.24 compat
                        {"titles", title + "|" + newTitle}

                    },
                    ActionOptions.All);

                CheckForErrors(result, "query");

                bool invalid;
                try
                {
                    XmlReader xr = CreateXmlReader(result);

                    bool readToPage = xr.ReadToFollowing("page");
                    invalid = xr.MoveToAttribute("invalid");
                    if (!xr.ReadToFollowing("tokens") && readToPage)
                    {
                        Page.MoveToken = xr.GetAttribute("movetoken");
                    }
                    else if (!readToPage)
                    {
                        throw new Exception("Cannot find <page> element");
                    }

                    Page.MoveToken = xr.GetAttribute("csrftoken");
                }
                catch (Exception ex)
                {
                    throw new BrokenXmlException(this, ex);
                }

                if (invalid)
                    throw new ApiException(this, "invalidnewtitle",
                        new ArgumentException("Target page invalid", "newTitle"));
            }

            if (Aborting) throw new AbortedException(this);

            var post = new Dictionary<string, string>
            {
                {"from", title},
                {"to", newTitle},
                {"token", Page.MoveToken},
                {"reason", reason},
                {"protections", ""},
            };

            post.AddIfTrue(moveTalk, "movetalk", null);
            post.AddIfTrue(noRedirect, "noredirect", null);
            //post.AddIfTrue(User.IsBot, "bot", null);
            post.AddIfTrue(watch, "watch", null);

            var result2 = HttpPost(
                new Dictionary<string, string>
                {
                    {"action", "move"}
                },
                post,
                ActionOptions.All);

            CheckForErrors(result2, "move");

            Reset();
        }
Beispiel #5
0
        public void Login(string username, string password, string domain)
        {
            if (string.IsNullOrEmpty(username)) throw new ArgumentException("Username required", "username");
            // if (string.IsNullOrEmpty(password)) throw new ArgumentException("Password required", "password");

            Reset();
            User = new UserInfo(); // we don't know for sure what will be our status in case of exception
            Cookies = new CookieContainer();

            // first see if we can get a login token via the new MediaWiki way using action=query&meta=tokens&type=login
            string result = HttpPost(
                new Dictionary<string, string>
                {
                    {"action", "query"},
                    {"meta", "tokens"},
                    {"type", "login"}
                },
                new Dictionary<string, string>());

            Tools.WriteDebug("API::Edit meta/tokens", result);

            /* Result format: <query><tokens logintoken="b0fc31b291ebf9999a8e9a4bfac8ef0456c44116+\"/></query> */
            XmlReader xr = CreateXmlReader(result);
            xr.ReadToFollowing("tokens");
            string token = xr.GetAttribute("logintoken");

            // first log in. If we got a logintoken then use it, this should be our only action=login in that case
            bool domainSet = !string.IsNullOrEmpty(domain);
            var post = new Dictionary<string, string>
            {
                {"lgname", username},
                {"lgpassword", password},
            };
            post.AddIfTrue(domainSet, "lgdomain", domain);
            post.AddIfTrue(!string.IsNullOrEmpty(token), "lgtoken", token);

            result = HttpPost(
                new Dictionary<string, string>
                {
                    {"action", "login"}
                },
                post
                );

            xr = CreateXmlReader(result);

            Tools.WriteDebug("API::Edit action/login", result);

            // if got token from new meta/tokens way, should now be logged in
            if(!string.IsNullOrEmpty(token))
            {
                xr.ReadToFollowing("login");
            }
            else // support the old way of first action=login to be told NeedToken and given token, then second action=login sending the token
            {
                // if we have login section in warnings don't want to look in there for the token
                if(result.Contains("<warnings>") && Regex.Matches(result, @"<login ").Count > 1)
                {
                    xr.ReadToFollowing("warnings");
                    xr.ReadToFollowing("login");
                }

                xr.ReadToFollowing("login");

                var attribute = xr.GetAttribute("result");

                if (attribute != null && attribute.Equals("NeedToken", StringComparison.InvariantCultureIgnoreCase))
                {
                    AdjustCookies();
                    token = xr.GetAttribute("token");

                    post.Add("lgtoken", token);
                    result = HttpPost(
                        new Dictionary<string, string> {{"action", "login"}},
                        post
                        );

                    Tools.WriteDebug("API::Edit action/login NeedToken", result);
                    xr = CreateXmlReader(result);
                    xr.ReadToFollowing("login");
                }
            }

            string status = xr.GetAttribute("result");
            if (status != null && !status.Equals("Success", StringComparison.InvariantCultureIgnoreCase))
            {
                throw new LoginException(this, status);
            }

            CheckForErrors(result, "login");
            AdjustCookies();

            RefreshUserInfo();
        }
Beispiel #6
0
        public void Delete(string title, string reason, bool watch)
        {
            if (string.IsNullOrEmpty(title)) throw new ArgumentException("Page name required", "title");
            if (string.IsNullOrEmpty(reason)) throw new ArgumentException("Deletion reason required", "reason");

            // Reset();
            Action = "delete";

            if (string.IsNullOrEmpty(Page.DeleteToken))
            {
                var result = HttpGet(
                    new Dictionary<string, string>
                    {
                        {"action", "query"},
                        {"prop", "info"},
                        {"meta", "tokens"}, // Since 1.24
                        {"type", "csrf"},
                        {"intoken", "delete"}, // Pre 1.24 compat
                        {"titles", title}

                    },
                    ActionOptions.All);

                CheckForErrors(result);

                try
                {
                    XmlReader xr = CreateXmlReader(result);
                    if (xr.ReadToFollowing("tokens"))
                    {
                        Page.DeleteToken = xr.GetAttribute("csrftoken");
                    }
                    else if (!xr.ReadToFollowing("page"))
                    {
                        throw new Exception("Cannot find <page> element");
                    }
                    else
                    {
                        Page.DeleteToken = xr.GetAttribute("deletetoken");
                    }
                }
                catch (Exception ex)
                {
                    throw new BrokenXmlException(this, ex);
                }
            }

            if (Aborting) throw new AbortedException(this);

            var post = new Dictionary<string, string>
            {
                {"title", title},
                {"token", Page.DeleteToken},
                {"reason", reason},
            };

            // post.AddIfTrue(User.IsBot, "bot", null);
            post.AddIfTrue(watch, "watch", null);
            var result2 = HttpPost(
                new Dictionary<string, string>
                {
                    {"action", "delete"}
                },
                post,
                ActionOptions.All);

            CheckForErrors(result2);

            Reset();
        }